具有现有主键约束的 alembic alter_column

alembic alter_column with existing primary key constraint

我需要用 alembic 改变列的类型。目前我的代码如下所示:

with op.batch_alter_table('annotations', schema=None) as batch_op:         
    batch_op.alter_column('anno_id', existing_type=sa.Integer(), type_=sa.String(36))
    

我的问题是 anno_id 是 table 的主键,虽然代码在 sqlite 上运行良好,但 MS SQL 拒绝命令: The object 'PK__annotati__2CC37C988C804EF7' is dependent on column 'anno_id'.DB-Lib error message 20018, severity 16

PK 约束是自动生成的,所以我无法提前知道它的名称。我需要做什么来识别约束并在更改列后删除并重新创建它?

更新,根据 Aaron Bertrand 在下面的回答,我能够在 alembic 中执行以下操作。不幸的是,正如他所说,我无法直接更改列以将其更改为字符串类型,所以我 dropped/recreated 它。

        if context.get_impl().bind.dialect.name == "mssql":
            batch_op.execute("""
            DECLARE @sql    nvarchar(max) = N'',
        @table  nvarchar(513) = QUOTENAME(N'annotations'),
        @column nvarchar(128) = QUOTENAME(N'anno_id');

SELECT @sql += N'ALTER TABLE dbo.' 
    + @table + N' DROP CONSTRAINT ' 
    + QUOTENAME(name) + N';'
  FROM sys.key_constraints
  WHERE type = N'PK' AND parent_object_id = OBJECT_ID(N'dbo.' + @table);

EXEC sys.sp_executesql @sql;
            """)
        batch_op.drop_column('anno_id')
        batch_op.add_column(sa.Column('anno_id', sa.String(36), nullable=False))
        batch_op.create_primary_key('annotations_pkey', ['anno_id'])

The PK constraint was auto-generated so I can't know in advance what its name is

您可以随时更改它(并更改 生成 table 的任何代码,这样它就不会将这些名称留给数据库来分配)。

EXEC sys.sp_rename 
     N'PK__anno_3213E83F046D1D25', -- you'll have to look this up once 
     N'PK_annotations', 
     N'OBJECT';

如果您无法修复数据库中的这个设计缺陷,那么您可以在需要时查找它:

SELECT name FROM sys.key_constraints
  WHERE type = N'PK'
    AND parent_object_id = OBJECT_ID(N'dbo.annotations');

您可以使用它来生成 drop/create 命令,以便您可以更改任何受影响的列,但它需要一些额外的上下文(例如 PK 适用于哪些列)。一个例子可能是:

DECLARE @sql    nvarchar(max) = N'',
        @table  nvarchar(513) = QUOTENAME(N'annotations'),
        @column nvarchar(128) = QUOTENAME(N'anno_id');

SELECT @sql += N'ALTER TABLE dbo.' 
    + @table + N' DROP CONSTRAINT ' 
    + QUOTENAME(name) + N';'
  FROM sys.key_constraints
  WHERE type = N'PK' AND parent_object_id = OBJECT_ID(N'dbo.' + @table);

SELECT @sql += char(13) + char(10) + N'ALTER TABLE dbo.' 
    + @table + N' ALTER COLUMN ' 
    + @column + N' decimal(14,4) NOT NULL;'
  FROM sys.key_constraints
  WHERE type = N'PK'   
    AND parent_object_id = OBJECT_ID(N'dbo.' + @table);

SELECT @sql += char(13) + char(10) + N'ALTER TABLE dbo.' 
    + @table 
    + N' ADD CONSTRAINT PK_Annotations PRIMARY KEY(' + @column + N');';

PRINT @sql;
--EXEC sys.sp_executesql @sql;

这将像这样生成 SQL(您可以 PRINT 在注释掉 EXEC 之前验证)。

ALTER TABLE dbo.[annotations] DROP CONSTRAINT [PK__annotati__2CC37C98902B2CC2];
ALTER TABLE dbo.[annotations] ALTER COLUMN [anno_id] decimal(14,4) NOT NULL;
ALTER TABLE dbo.[annotations] ADD CONSTRAINT PK_Annotations PRIMARY KEY([anno_id]);

这是 运行 在像 SQL Server Management Studio 这样的工具中。我非常怀疑您是否能够 直接从 alembic 构建这样的东西; ORM 并不完全以处理所有用例而闻名,只是处理最低限度的用例。一旦您需要在最低限度范围之外做一些甚至远程的事情,您就需要离开 ORM。

这不会是结束。因为我假设即使您不尝试以 可能 使 SQL 服务器认为会丢失数据的方式更改列(例如更改 numeric(14,4) -> numeric(12,2)),如果有指向它的外键、针对 table 的索引视图等,您也将无法删除 PK