Eva*_*oll 9 sql-server transaction ddl sql-server-2017
在 PostgreSQL 中,我可以创建一个包含一些测试数据的表,然后在事务中将其迁移到不同类型的新列,从而导致一个表重写COMMIT
,
CREATE TABLE foo ( a int );
INSERT INTO foo VALUES (1),(2),(3);
Run Code Online (Sandbox Code Playgroud)
其次是,
BEGIN;
ALTER TABLE foo ADD COLUMN b varchar;
UPDATE foo SET b = CAST(a AS varchar);
ALTER TABLE foo DROP COLUMN a;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
但是,Microsoft 的 SQL Server 中的相同内容似乎会产生错误。比较这个工作db fiddle,其中ADD
(列)命令在事务之外,
-- txn1
BEGIN TRANSACTION;
ALTER TABLE foo ADD b varchar;
COMMIT;
-- txn2
BEGIN TRANSACTION;
UPDATE foo SET b = CAST( a AS varchar );
ALTER TABLE foo DROP COLUMN a;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
对于这个不起作用的数据库小提琴,
-- txn1
BEGIN TRANSACTION;
ALTER TABLE foo ADD b varchar;
UPDATE foo SET b = CAST( a AS varchar );
ALTER TABLE foo DROP COLUMN a;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
而是错误
Msg 207 Level 16 State 1 Line 2
Invalid column name 'b'.
Run Code Online (Sandbox Code Playgroud)
有没有办法使这个事务可见,就 DDL 而言,表现得像 PostgreSQL?
Pau*_*ite 17
一般来说,没有。SQL Server 编译整批在执行之前在当前范围内,因此引用的实体必须存在(语句级重新编译也可能稍后发生)。主要的例外是延迟名称解析,但这适用于表,而不是列:
延迟名称解析只能在您引用不存在的表对象时使用。所有其他对象在创建存储过程时必须存在。例如,当您在存储过程中引用现有表时,您不能列出该表不存在的列。
常见的解决方法涉及动态代码(如 Joe 的回答),或将 DML 和 DDL 分成单独的批次。
对于这种特定情况,您还可以编写:
BEGIN TRANSACTION;
ALTER TABLE dbo.foo
ALTER COLUMN a varchar(11) NOT NULL
WITH (ONLINE = ON);
EXECUTE sys.sp_rename
@objname = N'dbo.foo.a',
@newname = N'b',
@objtype = 'COLUMN';
COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)
您仍然无法b
在同一批次和范围内访问重命名的列,但它确实完成了工作。
关于 SQL Server,有一种观点认为在事务中混合 DDL 和 DML 并不是一个好主意。过去曾出现错误,这样做会导致错误的日志记录和不可恢复的数据库。尽管如此,人们还是会这样做,尤其是使用临时表时。它可能会导致一些非常难以遵循的代码。
Joe*_*ish 12
这是你要找的吗?
BEGIN TRANSACTION;
ALTER TABLE foo ADD b varchar;
EXEC sp_executesql N'UPDATE foo SET b = CAST( a AS varchar )';
ALTER TABLE foo DROP COLUMN a;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
908 次 |
最近记录: |