[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M715VA158NJF2CFM5DPKZZM0":3,"skill-same:01M715VA158NJF2CFM5DPKZZM0":70},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":50,"commitSha":51,"skillDirectory":52,"forksCount":14,"repositoryHtmlUrl":53,"repositoryHomepageUrl":6,"ownerHtmlUrl":54,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":55,"install":56,"resources":69},"01M715VA158NJF2CFM5DPKZZM0","database-migration",null,"Master database schema and data migrations across ORMs (Sequelize, TypeORM, Prisma), including rollback strategies and zero-downtime deployments.","sapsapshen\u002Fai-memory-router","sapsapshen","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F20347959?v=4","01M6XDQEF1DP50DKF37H81J3J5","shenmian","01M6XDQEJPE41Y3DT2763X8ZJN",0,"MIT","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fcursor\u002Fdatabase-migration\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"database migration","migration","database","community","2026","unknown","sequelize","database schema","prisma","strategies","across","zero-downtime","typeorm","migrations","master","deployments","schema","rollback","data","ZeRO","2026-11-10 10:12:30",false,"---\nname: database-migration\ndescription: \"Master database schema and data migrations across ORMs (Sequelize, TypeORM, Prisma), including rollback strategies and zero-downtime deployments.\"\nrisk: unknown\nsource: community\ndate_added: \"2026-02-27\"\n---\n\n# Database Migration\n\nMaster database schema and data migrations across ORMs (Sequelize, TypeORM, Prisma), including rollback strategies and zero-downtime deployments.\n\n## Do not use this skill when\n\n- The task is unrelated to database migration\n- You need a different domain or tool outside this scope\n\n## Instructions\n\n- Clarify goals, constraints, and required inputs.\n- Apply relevant best practices and validate outcomes.\n- Provide actionable steps and verification.\n- If detailed examples are required, open `resources\u002Fimplementation-playbook.md`.\n\n## Use this skill when\n\n- Migrating between different ORMs\n- Performing schema transformations\n- Moving data between databases\n- Implementing rollback procedures\n- Zero-downtime deployments\n- Database version upgrades\n- Data model refactoring\n\n## ORM Migrations\n\n### Sequelize Migrations\n```javascript\n\u002F\u002F migrations\u002F20231201-create-users.js\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    await queryInterface.createTable('users', {\n      id: {\n        type: Sequelize.INTEGER,\n        primaryKey: true,\n        autoIncrement: true\n      },\n      email: {\n        type: Sequelize.STRING,\n        unique: true,\n        allowNull: false\n      },\n      createdAt: Sequelize.DATE,\n      updatedAt: Sequelize.DATE\n    });\n  },\n\n  down: async (queryInterface, Sequelize) => {\n    await queryInterface.dropTable('users');\n  }\n};\n\n\u002F\u002F Run: npx sequelize-cli db:migrate\n\u002F\u002F Rollback: npx sequelize-cli db:migrate:undo\n```\n\n### TypeORM Migrations\n```typescript\n\u002F\u002F migrations\u002F1701234567-CreateUsers.ts\nimport { MigrationInterface, QueryRunner, Table } from 'typeorm';\n\nexport class CreateUsers1701234567 implements MigrationInterface {\n  public async up(queryRunner: QueryRunner): Promise\u003Cvoid> {\n    await queryRunner.createTable(\n      new Table({\n        name: 'users',\n        columns: [\n          {\n            name: 'id',\n            type: 'int',\n            isPrimary: true,\n            isGenerated: true,\n            generationStrategy: 'increment'\n          },\n          {\n            name: 'email',\n            type: 'varchar',\n            isUnique: true\n          },\n          {\n            name: 'created_at',\n            type: 'timestamp',\n            default: 'CURRENT_TIMESTAMP'\n          }\n        ]\n      })\n    );\n  }\n\n  public async down(queryRunner: QueryRunner): Promise\u003Cvoid> {\n    await queryRunner.dropTable('users');\n  }\n}\n\n\u002F\u002F Run: npm run typeorm migration:run\n\u002F\u002F Rollback: npm run typeorm migration:revert\n```\n\n### Prisma Migrations\n```prisma\n\u002F\u002F schema.prisma\nmodel User {\n  id        Int      @id @default(autoincrement())\n  email     String   @unique\n  createdAt DateTime @default(now())\n}\n\n\u002F\u002F Generate migration: npx prisma migrate dev --name create_users\n\u002F\u002F Apply: npx prisma migrate deploy\n```\n\n## Schema Transformations\n\n### Adding Columns with Defaults\n```javascript\n\u002F\u002F Safe migration: add column with default\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    await queryInterface.addColumn('users', 'status', {\n      type: Sequelize.STRING,\n      defaultValue: 'active',\n      allowNull: false\n    });\n  },\n\n  down: async (queryInterface) => {\n    await queryInterface.removeColumn('users', 'status');\n  }\n};\n```\n\n### Renaming Columns (Zero Downtime)\n```javascript\n\u002F\u002F Step 1: Add new column\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    await queryInterface.addColumn('users', 'full_name', {\n      type: Sequelize.STRING\n    });\n\n    \u002F\u002F Copy data from old column\n    await queryInterface.sequelize.query(\n      'UPDATE users SET full_name = name'\n    );\n  },\n\n  down: async (queryInterface) => {\n    await queryInterface.removeColumn('users', 'full_name');\n  }\n};\n\n\u002F\u002F Step 2: Update application to use new column\n\n\u002F\u002F Step 3: Remove old column\nmodule.exports = {\n  up: async (queryInterface) => {\n    await queryInterface.removeColumn('users', 'name');\n  },\n\n  down: async (queryInterface, Sequelize) => {\n    await queryInterface.addColumn('users', 'name', {\n      type: Sequelize.STRING\n    });\n  }\n};\n```\n\n### Changing Column Types\n```javascript\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    \u002F\u002F For large tables, use multi-step approach\n\n    \u002F\u002F 1. Add new column\n    await queryInterface.addColumn('users', 'age_new', {\n      type: Sequelize.INTEGER\n    });\n\n    \u002F\u002F 2. Copy and transform data\n    await queryInterface.sequelize.query(`\n      UPDATE users\n      SET age_new = CAST(age AS INTEGER)\n      WHERE age IS NOT NULL\n    `);\n\n    \u002F\u002F 3. Drop old column\n    await queryInterface.removeColumn('users', 'age');\n\n    \u002F\u002F 4. Rename new column\n    await queryInterface.renameColumn('users', 'age_new', 'age');\n  },\n\n  down: async (queryInterface, Sequelize) => {\n    await queryInterface.changeColumn('users', 'age', {\n      type: Sequelize.STRING\n    });\n  }\n};\n```\n\n## Data Transformations\n\n### Complex Data Migration\n```javascript\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    \u002F\u002F Get all records\n    const [users] = await queryInterface.sequelize.query(\n      'SELECT id, address_string FROM users'\n    );\n\n    \u002F\u002F Transform each record\n    for (const user of users) {\n      const addressParts = user.address_string.split(',');\n\n      await queryInterface.sequelize.query(\n        `UPDATE users\n         SET street = :street,\n             city = :city,\n             state = :state\n         WHERE id = :id`,\n        {\n          replacements: {\n            id: user.id,\n            street: addressParts[0]?.trim(),\n            city: addressParts[1]?.trim(),\n            state: addressParts[2]?.trim()\n          }\n        }\n      );\n    }\n\n    \u002F\u002F Drop old column\n    await queryInterface.removeColumn('users', 'address_string');\n  },\n\n  down: async (queryInterface, Sequelize) => {\n    \u002F\u002F Reconstruct original column\n    await queryInterface.addColumn('users', 'address_string', {\n      type: Sequelize.STRING\n    });\n\n    await queryInterface.sequelize.query(`\n      UPDATE users\n      SET address_string = CONCAT(street, ', ', city, ', ', state)\n    `);\n\n    await queryInterface.removeColumn('users', 'street');\n    await queryInterface.removeColumn('users', 'city');\n    await queryInterface.removeColumn('users', 'state');\n  }\n};\n```\n\n## Rollback Strategies\n\n### Transaction-Based Migrations\n```javascript\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    const transaction = await queryInterface.sequelize.transaction();\n\n    try {\n      await queryInterface.addColumn(\n        'users',\n        'verified',\n        { type: Sequelize.BOOLEAN, defaultValue: false },\n        { transaction }\n      );\n\n      await queryInterface.sequelize.query(\n        'UPDATE users SET verified = true WHERE email_verified_at IS NOT NULL',\n        { transaction }\n      );\n\n      await transaction.commit();\n    } catch (error) {\n      await transaction.rollback();\n      throw error;\n    }\n  },\n\n  down: async (queryInterface) => {\n    await queryInterface.removeColumn('users', 'verified');\n  }\n};\n```\n\n### Checkpoint-Based Rollback\n```javascript\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    \u002F\u002F Create backup table\n    await queryInterface.sequelize.query(\n      'CREATE TABLE users_backup AS SELECT * FROM users'\n    );\n\n    try {\n      \u002F\u002F Perform migration\n      await queryInterface.addColumn('users', 'new_field', {\n        type: Sequelize.STRING\n      });\n\n      \u002F\u002F Verify migration\n      const [result] = await queryInterface.sequelize.query(\n        \"SELECT COUNT(*) as count FROM users WHERE new_field IS NULL\"\n      );\n\n      if (result[0].count > 0) {\n        throw new Error('Migration verification failed');\n      }\n\n      \u002F\u002F Drop backup\n      await queryInterface.dropTable('users_backup');\n    } catch (error) {\n      \u002F\u002F Restore from backup\n      await queryInterface.sequelize.query('DROP TABLE users');\n      await queryInterface.sequelize.query(\n        'CREATE TABLE users AS SELECT * FROM users_backup'\n      );\n      await queryInterface.dropTable('users_backup');\n      throw error;\n    }\n  }\n};\n```\n\n## Zero-Downtime Migrations\n\n### Blue-Green Deployment Strategy\n```javascript\n\u002F\u002F Phase 1: Make changes backward compatible\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    \u002F\u002F Add new column (both old and new code can work)\n    await queryInterface.addColumn('users', 'email_new', {\n      type: Sequelize.STRING\n    });\n  }\n};\n\n\u002F\u002F Phase 2: Deploy code that writes to both columns\n\n\u002F\u002F Phase 3: Backfill data\nmodule.exports = {\n  up: async (queryInterface) => {\n    await queryInterface.sequelize.query(`\n      UPDATE users\n      SET email_new = email\n      WHERE email_new IS NULL\n    `);\n  }\n};\n\n\u002F\u002F Phase 4: Deploy code that reads from new column\n\n\u002F\u002F Phase 5: Remove old column\nmodule.exports = {\n  up: async (queryInterface) => {\n    await queryInterface.removeColumn('users', 'email');\n  }\n};\n```\n\n## Cross-Database Migrations\n\n### PostgreSQL to MySQL\n```javascript\n\u002F\u002F Handle differences\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    const dialectName = queryInterface.sequelize.getDialect();\n\n    if (dialectName === 'mysql') {\n      await queryInterface.createTable('users', {\n        id: {\n          type: Sequelize.INTEGER,\n          primaryKey: true,\n          autoIncrement: true\n        },\n        data: {\n          type: Sequelize.JSON  \u002F\u002F MySQL JSON type\n        }\n      });\n    } else if (dialectName === 'postgres') {\n      await queryInterface.createTable('users', {\n        id: {\n          type: Sequelize.INTEGER,\n          primaryKey: true,\n          autoIncrement: true\n        },\n        data: {\n          type: Sequelize.JSONB  \u002F\u002F PostgreSQL JSONB type\n        }\n      });\n    }\n  }\n};\n```\n\n## Resources\n\n- **references\u002Form-switching.md**: ORM migration guides\n- **references\u002Fschema-migration.md**: Schema transformation patterns\n- **references\u002Fdata-transformation.md**: Data migration scripts\n- **references\u002Frollback-strategies.md**: Rollback procedures\n- **assets\u002Fschema-migration-template.sql**: SQL migration templates\n- **assets\u002Fdata-migration-script.py**: Data migration utilities\n- **scripts\u002Ftest-migration.sh**: Migration testing script\n\n## Best Practices\n\n1. **Always Provide Rollback**: Every up() needs a down()\n2. **Test Migrations**: Test on staging first\n3. **Use Transactions**: Atomic migrations when possible\n4. **Backup First**: Always backup before migration\n5. **Small Changes**: Break into small, incremental steps\n6. **Monitor**: Watch for errors during deployment\n7. **Document**: Explain why and how\n8. **Idempotent**: Migrations should be rerunnable\n\n## Common Pitfalls\n\n- Not testing rollback procedures\n- Making breaking changes without downtime strategy\n- Forgetting to handle NULL values\n- Not considering index performance\n- Ignoring foreign key constraints\n- Migrating too much data at once\n","# Database Migration\n\nMaster database schema and data migrations across ORMs (Sequelize, TypeORM, Prisma), including rollback strategies and zero-downtime deployments.\n\n## Do not use this skill when\n\n- The task is unrelated to database migration\n- You need a different domain or tool outside this scope\n\n## Instructions\n\n- Clarify goals, constraints, and required inputs.\n- Apply relevant best practices and validate outcomes.\n- Provide actionable steps and verification.\n- If detailed examples are required, open `resources\u002Fimplementation-playbook.md`.\n\n## Use this skill when\n\n- Migrating between different ORMs\n- Performing schema transformations\n- Moving data between databases\n- Implementing rollback procedures\n- Zero-downtime deployments\n- Database version upgrades\n- Data model refactoring\n\n## ORM Migrations\n\n### Sequelize Migrations\n```javascript\n\u002F\u002F migrations\u002F20231201-create-users.js\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    await queryInterface.createTable('users', {\n      id: {\n        type: Sequelize.INTEGER,\n        primaryKey: true,\n        autoIncrement: true\n      },\n      email: {\n        type: Sequelize.STRING,\n        unique: true,\n        allowNull: false\n      },\n      createdAt: Sequelize.DATE,\n      updatedAt: Sequelize.DATE\n    });\n  },\n\n  down: async (queryInterface, Sequelize) => {\n    await queryInterface.dropTable('users');\n  }\n};\n\n\u002F\u002F Run: npx sequelize-cli db:migrate\n\u002F\u002F Rollback: npx sequelize-cli db:migrate:undo\n```\n\n### TypeORM Migrations\n```typescript\n\u002F\u002F migrations\u002F1701234567-CreateUsers.ts\nimport { MigrationInterface, QueryRunner, Table } from 'typeorm';\n\nexport class CreateUsers1701234567 implements MigrationInterface {\n  public async up(queryRunner: QueryRunner): Promise\u003Cvoid> {\n    await queryRunner.createTable(\n      new Table({\n        name: 'users',\n        columns: [\n          {\n            name: 'id',\n            type: 'int',\n            isPrimary: true,\n            isGenerated: true,\n            generationStrategy: 'increment'\n          },\n          {\n            name: 'email',\n            type: 'varchar',\n            isUnique: true\n          },\n          {\n            name: 'created_at',\n            type: 'timestamp',\n            default: 'CURRENT_TIMESTAMP'\n          }\n        ]\n      })\n    );\n  }\n\n  public async down(queryRunner: QueryRunner): Promise\u003Cvoid> {\n    await queryRunner.dropTable('users');\n  }\n}\n\n\u002F\u002F Run: npm run typeorm migration:run\n\u002F\u002F Rollback: npm run typeorm migration:revert\n```\n\n### Prisma Migrations\n```prisma\n\u002F\u002F schema.prisma\nmodel User {\n  id        Int      @id @default(autoincrement())\n  email     String   @unique\n  createdAt DateTime @default(now())\n}\n\n\u002F\u002F Generate migration: npx prisma migrate dev --name create_users\n\u002F\u002F Apply: npx prisma migrate deploy\n```\n\n## Schema Transformations\n\n### Adding Columns with Defaults\n```javascript\n\u002F\u002F Safe migration: add column with default\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    await queryInterface.addColumn('users', 'status', {\n      type: Sequelize.STRING,\n      defaultValue: 'active',\n      allowNull: false\n    });\n  },\n\n  down: async (queryInterface) => {\n    await queryInterface.removeColumn('users', 'status');\n  }\n};\n```\n\n### Renaming Columns (Zero Downtime)\n```javascript\n\u002F\u002F Step 1: Add new column\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    await queryInterface.addColumn('users', 'full_name', {\n      type: Sequelize.STRING\n    });\n\n    \u002F\u002F Copy data from old column\n    await queryInterface.sequelize.query(\n      'UPDATE users SET full_name = name'\n    );\n  },\n\n  down: async (queryInterface) => {\n    await queryInterface.removeColumn('users', 'full_name');\n  }\n};\n\n\u002F\u002F Step 2: Update application to use new column\n\n\u002F\u002F Step 3: Remove old column\nmodule.exports = {\n  up: async (queryInterface) => {\n    await queryInterface.removeColumn('users', 'name');\n  },\n\n  down: async (queryInterface, Sequelize) => {\n    await queryInterface.addColumn('users', 'name', {\n      type: Sequelize.STRING\n    });\n  }\n};\n```\n\n### Changing Column Types\n```javascript\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    \u002F\u002F For large tables, use multi-step approach\n\n    \u002F\u002F 1. Add new column\n    await queryInterface.addColumn('users', 'age_new', {\n      type: Sequelize.INTEGER\n    });\n\n    \u002F\u002F 2. Copy and transform data\n    await queryInterface.sequelize.query(`\n      UPDATE users\n      SET age_new = CAST(age AS INTEGER)\n      WHERE age IS NOT NULL\n    `);\n\n    \u002F\u002F 3. Drop old column\n    await queryInterface.removeColumn('users', 'age');\n\n    \u002F\u002F 4. Rename new column\n    await queryInterface.renameColumn('users', 'age_new', 'age');\n  },\n\n  down: async (queryInterface, Sequelize) => {\n    await queryInterface.changeColumn('users', 'age', {\n      type: Sequelize.STRING\n    });\n  }\n};\n```\n\n## Data Transformations\n\n### Complex Data Migration\n```javascript\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    \u002F\u002F Get all records\n    const [users] = await queryInterface.sequelize.query(\n      'SELECT id, address_string FROM users'\n    );\n\n    \u002F\u002F Transform each record\n    for (const user of users) {\n      const addressParts = user.address_string.split(',');\n\n      await queryInterface.sequelize.query(\n        `UPDATE users\n         SET street = :street,\n             city = :city,\n             state = :state\n         WHERE id = :id`,\n        {\n          replacements: {\n            id: user.id,\n            street: addressParts[0]?.trim(),\n            city: addressParts[1]?.trim(),\n            state: addressParts[2]?.trim()\n          }\n        }\n      );\n    }\n\n    \u002F\u002F Drop old column\n    await queryInterface.removeColumn('users', 'address_string');\n  },\n\n  down: async (queryInterface, Sequelize) => {\n    \u002F\u002F Reconstruct original column\n    await queryInterface.addColumn('users', 'address_string', {\n      type: Sequelize.STRING\n    });\n\n    await queryInterface.sequelize.query(`\n      UPDATE users\n      SET address_string = CONCAT(street, ', ', city, ', ', state)\n    `);\n\n    await queryInterface.removeColumn('users', 'street');\n    await queryInterface.removeColumn('users', 'city');\n    await queryInterface.removeColumn('users', 'state');\n  }\n};\n```\n\n## Rollback Strategies\n\n### Transaction-Based Migrations\n```javascript\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    const transaction = await queryInterface.sequelize.transaction();\n\n    try {\n      await queryInterface.addColumn(\n        'users',\n        'verified',\n        { type: Sequelize.BOOLEAN, defaultValue: false },\n        { transaction }\n      );\n\n      await queryInterface.sequelize.query(\n        'UPDATE users SET verified = true WHERE email_verified_at IS NOT NULL',\n        { transaction }\n      );\n\n      await transaction.commit();\n    } catch (error) {\n      await transaction.rollback();\n      throw error;\n    }\n  },\n\n  down: async (queryInterface) => {\n    await queryInterface.removeColumn('users', 'verified');\n  }\n};\n```\n\n### Checkpoint-Based Rollback\n```javascript\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    \u002F\u002F Create backup table\n    await queryInterface.sequelize.query(\n      'CREATE TABLE users_backup AS SELECT * FROM users'\n    );\n\n    try {\n      \u002F\u002F Perform migration\n      await queryInterface.addColumn('users', 'new_field', {\n        type: Sequelize.STRING\n      });\n\n      \u002F\u002F Verify migration\n      const [result] = await queryInterface.sequelize.query(\n        \"SELECT COUNT(*) as count FROM users WHERE new_field IS NULL\"\n      );\n\n      if (result[0].count > 0) {\n        throw new Error('Migration verification failed');\n      }\n\n      \u002F\u002F Drop backup\n      await queryInterface.dropTable('users_backup');\n    } catch (error) {\n      \u002F\u002F Restore from backup\n      await queryInterface.sequelize.query('DROP TABLE users');\n      await queryInterface.sequelize.query(\n        'CREATE TABLE users AS SELECT * FROM users_backup'\n      );\n      await queryInterface.dropTable('users_backup');\n      throw error;\n    }\n  }\n};\n```\n\n## Zero-Downtime Migrations\n\n### Blue-Green Deployment Strategy\n```javascript\n\u002F\u002F Phase 1: Make changes backward compatible\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    \u002F\u002F Add new column (both old and new code can work)\n    await queryInterface.addColumn('users', 'email_new', {\n      type: Sequelize.STRING\n    });\n  }\n};\n\n\u002F\u002F Phase 2: Deploy code that writes to both columns\n\n\u002F\u002F Phase 3: Backfill data\nmodule.exports = {\n  up: async (queryInterface) => {\n    await queryInterface.sequelize.query(`\n      UPDATE users\n      SET email_new = email\n      WHERE email_new IS NULL\n    `);\n  }\n};\n\n\u002F\u002F Phase 4: Deploy code that reads from new column\n\n\u002F\u002F Phase 5: Remove old column\nmodule.exports = {\n  up: async (queryInterface) => {\n    await queryInterface.removeColumn('users', 'email');\n  }\n};\n```\n\n## Cross-Database Migrations\n\n### PostgreSQL to MySQL\n```javascript\n\u002F\u002F Handle differences\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    const dialectName = queryInterface.sequelize.getDialect();\n\n    if (dialectName === 'mysql') {\n      await queryInterface.createTable('users', {\n        id: {\n          type: Sequelize.INTEGER,\n          primaryKey: true,\n          autoIncrement: true\n        },\n        data: {\n          type: Sequelize.JSON  \u002F\u002F MySQL JSON type\n        }\n      });\n    } else if (dialectName === 'postgres') {\n      await queryInterface.createTable('users', {\n        id: {\n          type: Sequelize.INTEGER,\n          primaryKey: true,\n          autoIncrement: true\n        },\n        data: {\n          type: Sequelize.JSONB  \u002F\u002F PostgreSQL JSONB type\n        }\n      });\n    }\n  }\n};\n```\n\n## Resources\n\n- **references\u002Form-switching.md**: ORM migration guides\n- **references\u002Fschema-migration.md**: Schema transformation patterns\n- **references\u002Fdata-transformation.md**: Data migration scripts\n- **references\u002Frollback-strategies.md**: Rollback procedures\n- **assets\u002Fschema-migration-template.sql**: SQL migration templates\n- **assets\u002Fdata-migration-script.py**: Data migration utilities\n- **scripts\u002Ftest-migration.sh**: Migration testing script\n\n## Best Practices\n\n1. **Always Provide Rollback**: Every up() needs a down()\n2. **Test Migrations**: Test on staging first\n3. **Use Transactions**: Atomic migrations when possible\n4. **Backup First**: Always backup before migration\n5. **Small Changes**: Break into small, incremental steps\n6. **Monitor**: Watch for errors during deployment\n7. **Document**: Explain why and how\n8. **Idempotent**: Migrations should be rerunnable\n\n## Common Pitfalls\n\n- Not testing rollback procedures\n- Making breaking changes without downtime strategy\n- Forgetting to handle NULL values\n- Not considering index performance\n- Ignoring foreign key constraints\n- Migrating too much data at once\n",{"name":5,"risk":29,"source":27,"date_added":49,"description":7},"2026-02-27","main","7a38d8e7073e61c16eda85681cb32fb68a8657a5","skills-store\u002Fcursor\u002Fdatabase-migration","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router","https:\u002F\u002Fgithub.com\u002Fsapsapshen","2026-04-14 21:12:12",{"source":8,"skillName":5,"commands":57,"prompt":67,"zipAvailable":68},[58,61,64],{"tool":59,"command":60},"npx","npx skills add sapsapshen\u002Fai-memory-router --skill database-migration",{"tool":62,"command":63},"bunx","bunx skills add sapsapshen\u002Fai-memory-router --skill database-migration",{"tool":65,"command":66},"pnpm","pnpm dlx skills add sapsapshen\u002Fai-memory-router --skill database-migration","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fcursor\u002Fdatabase-migration\u002FSKILL.md",true,[],[71,95,119,129,142,159],{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":82,"publishedAt":44},"01M71ATQRKG48SH06FQ697T5G9","wechat-search","Search WeChat Official Account articles using OpenClaw's web search and fetch capabilities with compliance-focused design.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fwechat-search-release\u002FSKILL.md",[77],{"categoryNo":78,"categoryName":79,"taxonomyType":80,"taxonomyTypeDesc":81},"01JBSEEDCAT000000000000037","律师与法务","PROFESSION","适用职业",[83,84,85,86,87,88,89,90,91,92,93,94],"wechat search","wechat","search","capabilities","openclaw","design","web search","compliance","web","official","fetch","with",{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":101,"publishedAt":44},"01M71ATNAVGZB35PFYM2GC5N00","trend-tap","Real-time trending topics aggregator across 7 platforms (X\u002FTwitter, Reddit, Google Trends, Hacker News, Zhihu, Bilibili, Weibo). Trigger: when user says 'trend', 'trends', 'trend tap', '热点', '热搜', or asks what is trending\u002Fhot. Do NOT use web_search — this skill already fetches live data from all platforms with zero API keys.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftrend-tap\u002FSKILL.md",[],[102,103,104,105,106,107,85,108,109,110,111,112,113,114,42,115,116,117,34,118],"trend","trend tap","tap","api keys","Bash","time","trending","hacker","aggregator","do","when","brew","live","google","install","read","api",{"skillNo":120,"name":121,"nameZh":6,"description":122,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":123,"categories":124,"keywords":125,"publishedAt":44},"01M71ATJ9AM5KM15AA8PS302EB","trend-radar","Real-time trending topics aggregator across 7 platforms (X\u002FTwitter, Reddit, Google Trends, Hacker News, Zhihu, Bilibili, Weibo). Trigger: when user says 'trend', 'trends', 'trend pulse', '热点', '热搜', or asks what is trending\u002Fhot. Do NOT use web_search — this skill already fetches live data from all platforms with zero API keys.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftrend-radar\u002FSKILL.md",[],[126,127,102,113,114,42,115,106,85,117,128,34,118,112,110,109,108,116,107,105],"trend radar","radar","pulse",{"skillNo":130,"name":131,"nameZh":6,"description":132,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":133,"categories":134,"keywords":135,"publishedAt":44},"01M71ATGF095N3EJM9G8SD0BB5","tech-news-daily","每日科技新闻汇——从全球多个 RSS 源抓取最新科技新闻，翻译（英→中）、去重、排版成每日简报。当用户询问科技新闻、科技新鲜事、每日科技、技术动态、Daily Tech News 或类似表达时触发此技能。","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftech-news-daily\u002FSKILL.md",[],[136,137,138,139,140,141],"news","tech","tech news daily","daily","rss","翻译",{"skillNo":143,"name":144,"nameZh":6,"description":145,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":146,"categories":147,"keywords":151,"publishedAt":44},"01M71ATFAEAMQ17M70NQNY7SR8","summarize","Summarize URLs or files with the summarize CLI (web, PDFs, images, audio, YouTube).","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fsummarize\u002FSKILL.md",[148],{"categoryNo":149,"categoryName":150,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000021","资料检索与研究",[144,91,152,153,154,94,113,155,156,157,104,158,116],"Audio","cli","youtube","images","https","files","pdfs",{"skillNo":160,"name":161,"nameZh":6,"description":162,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":163,"categories":164,"keywords":165,"publishedAt":44},"01M71ATDNS3R0M4H936PACQAWZ","sonoscli","Control Sonos speakers (discover\u002Fstatus\u002Fplay\u002Fvolume\u002Fgroup).","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fsonoscli\u002FSKILL.md",[],[161,166,167,168,116,169,156,170,171,172,173,174],"status","play","go","latest","github","discover","control","cmd","volume"]