1. 程式人生 > >SQL 管理事務處理

SQL 管理事務處理

事務處理
        使用事務處理通過確保成批的SQL操作要麼完全執行,要麼完全不執行,來維護資料庫的完整性。
        關係資料庫把資料儲存在多個表中,使資料更容易操縱、維護和重用。不用深究如何及為什麼進行關係資料庫的設計,在某種程度上說,設計良好的資料庫模式都是關聯的。
        訂單儲存在Orders和OrderItems兩個表中:Orders儲存實際的訂單,OrderItems儲存訂購的各項物品。這兩個表使用稱為主鍵(參閱第1課)的唯一ID互相關聯,又與包含客戶和產品資訊的其他表相關聯。
給系統新增訂單的過程如下:
    1. 檢查資料庫中是否存在相應的顧客,如果不存在,新增他;

    2. 檢索顧客的ID;
    3. 在Orders表新增一行,它與顧客ID相關聯;
    4. 檢索Orders表中賦予的新訂單ID;
    5. 為訂購的每個物品在OrderItems表中新增一行,通過檢索出來的ID把它與Orders表關聯(並且通過產品ID與Products表關聯)。
        現在假設由於某種資料庫故障(如超出磁碟空間、安全限制、表鎖等),這個過程無法完成。資料庫中的資料會出現什麼情況?
        如果故障發生在新增顧客之後,新增Orders表之前,則不會有什麼問題。某些顧客沒有訂單是完全合法的。重新執行此過程時,所插入的顧客記錄將被檢索和使用。可以有效地從出故障的地方開始執行此過程。
但是,如果故障發生在插入Orders行之後,新增OrderItems行之前,怎麼辦?現在,資料庫中有一個空訂單。更糟的是,如果系統在新增OrderItems行之時出現故障,怎麼辦?結果是資料庫中存在不完整的訂單,而你還不知道。
        如何解決這種問題?這就需要使用事務處理了。事務處理是一種機制,用來管理必須成批執行的SQL操作,保證資料庫不包含不完整的操作結果。利用事務處理,可以保證一組操作不會中途停止,它們要麼完全執行,要麼完全不執行(除非明確指示)。如果沒有錯誤發生,整組語句提交給(寫到)資料庫表;如果發生錯誤,則進行回退(撤銷),將資料庫恢復到某個已知且安全的狀態。
再看這個例子,這次我們說明這一過程是如何工作的:

    1. 檢查資料庫中是否存在相應的顧客,如果不存在,新增他;
    2. 提交顧客資訊;
    3. 檢索顧客的ID;
    4. 在Orders表中新增一行;
    5. 如果向Orders表新增行時出現故障,回退;
    6. 檢索Orders表中賦予的新訂單ID;
    7. 對於訂購的每項物品,新增新行到OrderItems表;
    8. 如果向OrderItems新增行時出現故障,回退所有新增的OrderItems行和Orders行。
在使用事務處理時,有幾個反覆出現的關鍵詞。下面是關於事務處理需要知道的幾個術語:
    1、事務(transaction)指一組SQL語句;
    2、回退(rollback)指撤銷指定SQL語句的過程;
    3、提交(commit)指將未儲存的SQL語句結果寫入資料庫表;
    4、保留點(savepoint)指事務處理中設定的臨時佔位符(placeholder),可以對它釋出回退(與回退整個事務處理不同)。

控制事務處理
        管理事務的關鍵在於將SQL語句組分解為邏輯塊,並明確規定資料何時應該回退,何時不應該回退。
有的DBMS要求明確標識事務處理塊的開始和結束。如在SQL Server中,標識如下:
輸入▼
BEGIN TRANSACTION
...
COMMIT TRANSACTION
分析▼
在這個例子中,BEGIN  TRANSACTION和COMMIT  TRANSACTION語句之間的SQL必須完全執行或者完全不執行。
MariaDB和MySQL中等同的程式碼為:
輸入▼
START TRANSACTION
...
Oracle使用的語法:
輸入▼
SET TRANSACTION
...
PostgreSQL使用ANSI SQL語法:
輸入▼
BEGIN
...
        其他DBMS採用上述語法的變體。你會發現,多數實現沒有明確標識事務處理在何處結束。事務一直存在,直到被中斷。通常,COMMITT用於儲存更改,ROLLBA CK用於撤銷,詳述如下。

使用ROLLBACK
        SQL的ROLLBACK命令用來回退(撤銷)SQL語句,請看下面的語句:
輸入▼
DELETE  FROM  Orders;
ROLLBACK;
分析▼
        在此例子中,執行DELETE操作,然後用ROLLBA CK語句撤銷。雖然這不是最有用的例子,但它的確能夠說明,在事務處理塊中,DELETE操作(與INSERT和UPDA TE操作一樣)並不是最終的結果。

使用COMMIT
        一般的SQL語句都是針對資料庫表直接執行和編寫的。這就是所謂的隱式提交(implicit commit),即提交(寫或儲存)操作是自動進行的。
        在事務處理塊中,提交不會隱式進行。不過,不同DBMS的做法有所不同。有的DBMS按隱式提交處理事務端,有的則不這樣。
進行明確的提交,使用COMMIT語句。下面是一個SQL Server的例子:
輸入▼
BEGIN  TRANSACTION
DELETE  OrderItems  WHERE order_num = 12345
DELETE  Orders  WHERE order_num = 12345
COMMIT TRANSACTION
分析▼
        在這個SQL Server例子中,從系統中完全刪除訂單12345。因為涉及更新兩個資料庫表Orders和OrderItems,所以使用事務處理塊來保證訂單不被部分刪除。最後的COMMIT語句僅在不出錯時寫出更改。如果第一條DELETE起作用,但第二條失敗,則DELETE不會提交。
為在Oracle中完成相同的工作,可如下進行:
輸入▼
SET  TRANSACTION
DELETE  OrderItems WHERE order_num = 12345;
DELETE  Orders WHERE order_num = 12345;
COMMIT;

使用保留點
        使用簡單的ROLLBACK和COMMIT語句,就可以寫入或撤銷整個事務。但是,只對簡單的事務才能這樣做,複雜的事務可能需要部分提交或回退。
        例如前面描述的新增訂單的過程就是一個事務。如果發生錯誤,只需要返回到新增Orders行之前即可。不需要回退到Customers表(如果存在的話)。
        要支援回退部分事務,必須在事務處理塊中的合適位置放置佔位符。這樣,如果需要回退,可以回退到某個佔位符。
在SQL中,這些佔位符稱為保留點。在MariaDB、MySQL和Oracle中建立佔位符,可使用SA VEPOINT語句:
輸入▼
SAVEPOINT  delete1;
在SQL  Server中,如下進行:
輸入▼
SAVE  TRANSACTION delete1;
        每個保留點都要取能夠標識它的唯一名字,以便在回退時,DBMS知道回退到何處。要回退到本例給出的保留點,在SQL Server中可如下進行:
輸入▼
ROLLBACK TRANSACTION delete1;
在MariaDB、MySQL和Oracle中,如下進行:
輸入▼
ROLLBACK TO delete1;
下面是一個完整的SQL Server例子:
輸入▼
BEGIN TRANSACTION
INSERT INTO Customers(cust_id, cust_name)
VALUES('1000000010', 'Toys Emporium');
SAVE TRANSACTION StartOrder;
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20100,'2001/12/1','1000000010');
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20100, 1, 'BR01', 100, 5.49);
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20100, 2, 'BR03', 100, 10.99);
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
COMMIT TRANSACTION
分析▼
        這裡的事務處理塊中包含了4條INSERT語句。在第一條INSERT語句之後定義了一個保留點,因此,如果後面的任何一個INSERT操作失敗,事務處理最近回退到這裡。在SQL Server中,可檢查一個名為@@ERROR的變數,看操作是否成功。(其他DBMS使用不同的函式或變數返回此資訊。)如果@@ERROR返回一個非0的值,表示有錯誤發生,事務處理回退到保留點。如果整個事務處理成功,釋出COMMIT以保留資料。
提示:保留點越多越好
可以在SQL程式碼中設定任意多的保留點,越多越好。為什麼呢?因為保留點越多,你就越能靈活地進行回退。