Sql-Server
SQL Server 在送出之前是否允許(使可見)事務內部的 DDL 到事務?
在 PostgreSQL 中,我可以創建一個包含一些測試數據的表,然後在事務中將其遷移到不同類型的新列,從而導致一個表重寫
COMMIT
,CREATE TABLE foo ( a int ); INSERT INTO foo VALUES (1),(2),(3);
其次是,
BEGIN; ALTER TABLE foo ADD COLUMN b varchar; UPDATE foo SET b = CAST(a AS varchar); ALTER TABLE foo DROP COLUMN a; COMMIT;
但是,在 Microsoft 的 SQL Server 中,同樣的事情似乎會產生錯誤。比較這個工作db fiddle,其中
ADD
(column) 命令在事務之外,-- 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;
到這個不起作用的數據庫小提琴,
-- txn1 BEGIN TRANSACTION; ALTER TABLE foo ADD b varchar; UPDATE foo SET b = CAST( a AS varchar ); ALTER TABLE foo DROP COLUMN a; COMMIT;
而是錯誤
Msg 207 Level 16 State 1 Line 2 Invalid column name 'b'.
關於 DDL,是否有讓這個事務可見的行為,就像 PostgreSQL 一樣?
一般來說,沒有。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;
您仍然無法
b
在同一批次和範圍內訪問重命名的列,但它確實完成了工作。對於 SQL Server,有一種觀點認為在事務中混合 DDL 和 DML 並不是一個好主意。過去有一些錯誤,這樣做會導致不正確的日誌記錄和無法恢復的數據庫。儘管如此,人們還是會這樣做,尤其是使用臨時表。它可能會導致一些非常難以理解的程式碼。