diff --git a/backend/migrations/2026_01_29_141058_convert_orders_to_hypertable.php b/backend/migrations/2026_01_29_141058_convert_orders_to_hypertable.php new file mode 100644 index 0000000..e482dd1 --- /dev/null +++ b/backend/migrations/2026_01_29_141058_convert_orders_to_hypertable.php @@ -0,0 +1,90 @@ +dropForeign(['order_id']); + }); + + // 2. 删除原有的主键约束 + Schema::getConnection()->statement('ALTER TABLE orders DROP CONSTRAINT orders_pkey'); + + // 3. 删除原有的唯一约束 + Schema::getConnection()->statement('ALTER TABLE orders DROP CONSTRAINT orders_store_platform_order_unique'); + + // 4. 创建新的复合主键(包含分区键) + Schema::getConnection()->statement('ALTER TABLE orders ADD PRIMARY KEY (id, created_date)'); + + // 5. 转换为 hypertable(按年分区) + Schema::getConnection()->statement(" + SELECT create_hypertable('orders', 'created_date', + chunk_time_interval => INTERVAL '1 year', + migrate_data => true + ) + "); + + // 6. 创建新的唯一约束(包含分区键) + Schema::getConnection()->statement(' + ALTER TABLE orders ADD CONSTRAINT orders_store_platform_order_unique + UNIQUE (store_id, platform_order_id, created_date) + '); + } + + /** + * Reverse the migrations. + * + * 警告:回滚 hypertable 会丢失分区结构,数据会保留但需要手动处理 + */ + public function down(): void + { + // 注意:TimescaleDB 不支持直接将 hypertable 转回普通表 + // 需要创建新表、迁移数据、删除 hypertable、重命名新表 + + // 1. 创建临时表 + Schema::getConnection()->statement(' + CREATE TABLE orders_temp AS SELECT * FROM orders + '); + + // 2. 删除 hypertable + Schema::getConnection()->statement('DROP TABLE orders'); + + // 3. 重命名临时表 + Schema::getConnection()->statement('ALTER TABLE orders_temp RENAME TO orders'); + + // 4. 重建原有约束 + Schema::getConnection()->statement('ALTER TABLE orders ADD PRIMARY KEY (id)'); + Schema::getConnection()->statement(' + ALTER TABLE orders ADD CONSTRAINT orders_store_platform_order_unique + UNIQUE (store_id, platform_order_id) + '); + + // 5. 重建外键 + Schema::table('order_items', function ($table) { + $table->foreign('order_id') + ->references('id') + ->on('orders') + ->onDelete('cascade'); + }); + + // 6. 重建序列(如果需要) + Schema::getConnection()->statement(" + SELECT setval('orders_id_seq', (SELECT MAX(id) FROM orders)) + "); + } +};