Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Hướng dẫn giao dịch SQL

Trong SQL, các giao dịch được sử dụng để duy trì tính toàn vẹn của dữ liệu bằng cách đảm bảo rằng một chuỗi các câu lệnh SQL thực thi hoàn toàn hoặc không thực thi hoàn toàn.

Các giao dịch quản lý chuỗi các câu lệnh SQL phải được thực thi như một đơn vị công việc duy nhất, để cơ sở dữ liệu không bao giờ chứa kết quả của các hoạt động từng phần.

Khi một giao dịch thực hiện nhiều thay đổi đối với cơ sở dữ liệu, tất cả các thay đổi sẽ thành công khi giao dịch được cam kết hoặc tất cả các thay đổi sẽ được hoàn tác khi giao dịch được khôi phục.

Khi nào thì sử dụng giao dịch?

Các giao dịch là tối quan trọng trong các tình huống mà tính toàn vẹn của dữ liệu sẽ gặp rủi ro trong trường hợp bất kỳ một trong chuỗi các câu lệnh SQL nào bị lỗi.

Ví dụ:nếu bạn đang chuyển tiền từ tài khoản ngân hàng này sang tài khoản ngân hàng khác, bạn cần phải khấu trừ tiền từ tài khoản này và cộng vào tài khoản kia. Bạn sẽ không muốn nó thất bại giữa chừng, nếu không, tiền có thể được ghi nợ từ tài khoản này nhưng không được ghi có vào tài khoản kia.

Các lý do có thể gây ra lỗi có thể bao gồm không đủ tiền, số tài khoản không hợp lệ, lỗi phần cứng, v.v.

Vì vậy, bạn không không muốn ở trong tình huống mà nó vẫn như thế này:

Debit account 1 (Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)

Đó sẽ là thực sự tồi tệ. Cơ sở dữ liệu sẽ có dữ liệu không nhất quán và tiền sẽ tan thành mây khói. Sau đó, ngân hàng sẽ mất một khách hàng (ngân hàng có thể sẽ mất tất cả khách hàng của mình nếu điều này tiếp tục xảy ra) và bạn sẽ mất việc.

Để cứu công việc của mình, bạn có thể sử dụng một giao dịch sẽ diễn ra như sau:

START TRANSACTION
Debit account 1
Credit account 2
Record transaction in transaction journal
END TRANSACTION 

Bạn có thể viết logic có điều kiện bên trong giao dịch đó để khôi phục giao dịch nếu có gì sai.

Ví dụ:nếu có sự cố xảy ra giữa ghi nợ tài khoản 1 và ghi có tài khoản 2, toàn bộ giao dịch sẽ được khôi phục.

Do đó, sẽ chỉ có hai kết quả có thể xảy ra:

Debit account 1 (Not Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)

Hoặc:

Debit account 1 (Done)
Credit account 2 (Done)
Record transaction in transaction journal (Done)

Đây là một mô tả đơn giản, nhưng nó là một minh họa cổ điển về cách hoạt động của các giao dịch SQL. Các giao dịch SQL có ACID.

Loại giao dịch

Các giao dịch SQL có thể được chạy ở các chế độ sau.

Chế độ giao dịch Mô tả
Giao dịch tự động gửi Mỗi câu lệnh riêng lẻ là một giao dịch.
Giao dịch ngầm Một giao dịch mới được bắt đầu một cách ngầm định khi giao dịch trước đó hoàn thành, nhưng mỗi giao dịch được hoàn thành một cách rõ ràng, thường với một COMMIT hoặc ROLLBACK tùy thuộc vào DBMS.
Giao dịch rõ ràng Bắt đầu rõ ràng bằng một dòng chẳng hạn như START TRANSACTION , START TRANSACTION hoặc tương tự, tùy thuộc vào DBMS, và cam kết rõ ràng hoặc quay lại với các tuyên bố có liên quan.
Giao dịch theo loạt Chỉ áp dụng cho nhiều bộ kết quả hoạt động (MARS). Một giao dịch rõ ràng hoặc ẩn bắt đầu trong một phiên MARS sẽ trở thành một giao dịch có phạm vi hàng loạt.

Các chế độ và tùy chọn chính xác có sẵn có thể phụ thuộc vào DBMS. Bảng này phác thảo các chế độ giao dịch có sẵn trong SQL Server.

Trong bài viết này, chúng tôi chủ yếu tập trung vào các giao dịch rõ ràng.

Xem Cách giao dịch ngầm hoạt động trong SQL Server để thảo luận về sự khác biệt giữa giao dịch ngầm định và tự động gửi.

Sytnax

Bảng sau đây phác thảo cú pháp cơ bản để bắt đầu và kết thúc một giao dịch rõ ràng trong một số DBMS phổ biến hơn.

DBMS Cú pháp giao dịch rõ ràng
MySQL, MariaDB, PostgreSQL Các giao dịch rõ ràng bắt đầu bằng START TRANSACTION hoặc BEGIN tuyên bố. COMMIT cam kết giao dịch hiện tại, thực hiện các thay đổi của nó là vĩnh viễn. ROLLBACK quay trở lại giao dịch hiện tại, hủy các thay đổi của nó.
SQLite Các giao dịch rõ ràng bắt đầu bằng BEGIN TRANSACTION và kết thúc bằng COMMIT hoặc ROLLBACK tuyên bố. Cũng có thể kết thúc bằng END tuyên bố.
Máy chủ SQL Các giao dịch rõ ràng bắt đầu bằng BEGIN TRANSACTION và kết thúc bằng COMMIT hoặc ROLLBACK tuyên bố.
Oracle Các giao dịch rõ ràng bắt đầu bằng SET TRANSACTION và kết thúc bằng COMMIT hoặc ROLLBACK tuyên bố.

Trong nhiều trường hợp, các từ khóa nhất định là tùy chọn khi sử dụng các giao dịch rõ ràng. Ví dụ:trong SQL Server và SQLite, bạn có thể chỉ cần sử dụng BEGIN (chứ không phải START TRANSACTION ) và / hoặc bạn có thể kết thúc bằng COMMIT TRANSACTION (trái ngược với chỉ COMMIT ).

Ngoài ra còn có nhiều từ khóa và tùy chọn khác mà bạn có thể chỉ định khi tạo giao dịch, vì vậy hãy xem tài liệu về DBMS của bạn để biết cú pháp đầy đủ.

Ví dụ về giao dịch SQL

Dưới đây là ví dụ về một giao dịch đơn giản trong SQL Server:

BEGIN TRANSACTION
    DELETE OrderItems WHERE OrderId = 5006;
    DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION;

Trong trường hợp này, thông tin đơn hàng bị xóa khỏi hai bảng. Cả hai câu lệnh đều được coi là một đơn vị công việc.

Chúng tôi có thể viết logic có điều kiện vào giao dịch của mình để khiến nó khôi phục trong trường hợp xảy ra lỗi.

Đặt tên cho một giao dịch

Một số DBMS cho phép bạn cung cấp tên cho các giao dịch của mình. Trong SQL Server, bạn có thể thêm tên đã chọn của mình sau BEGINCOMMIT tuyên bố.

BEGIN TRANSACTION MyTransaction
    DELETE OrderItems WHERE OrderId = 5006;
    DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION MyTransaction;

Ví dụ khôi phục giao dịch SQL 1

Đây là ví dụ trước một lần nữa, nhưng có thêm một số mã. Mã bổ sung được sử dụng để khôi phục giao dịch trong trường hợp có lỗi.:

BEGIN TRANSACTION MyTransaction

  BEGIN TRY

    DELETE OrderItems WHERE OrderId = 5006;
    DELETE Orders WHERE OrderId = 5006;

    COMMIT TRANSACTION MyTransaction

  END TRY

  BEGIN CATCH

      ROLLBACK TRANSACTION MyTransaction

  END CATCH

TRY...CATCH câu lệnh thực hiện xử lý lỗi trong SQL Server. Bạn có thể bao gồm bất kỳ nhóm câu lệnh T-SQL nào trong TRY khối. Sau đó, nếu xảy ra lỗi trong TRY khối, điều khiển được chuyển cho một nhóm câu lệnh khác nằm trong CATCH khối.

Trong trường hợp này, chúng tôi sử dụng CATCH khối để khôi phục giao dịch. Cho nó nằm trong CATCH chặn, khôi phục chỉ xảy ra nếu có lỗi.

Ví dụ khôi phục giao dịch SQL 2

Hãy xem xét kỹ hơn cơ sở dữ liệu mà chúng tôi vừa xóa các hàng khỏi đó.

Trong ví dụ trước, chúng tôi đã xóa các hàng khỏi OrdersOrderItems bảng trong cơ sở dữ liệu sau:

Trong cơ sở dữ liệu này, mỗi khi khách hàng đặt hàng, một hàng sẽ được chèn vào Orders bảng và một hoặc nhiều hàng trong OrderItems bàn. Số hàng được chèn vào OrderItems phụ thuộc vào số lượng sản phẩm khác nhau mà khách hàng đặt hàng.

Ngoài ra, nếu đó là khách hàng mới, một hàng mới sẽ được chèn vào Customers bảng.

Trong trường hợp đó, các hàng cần được chèn vào ba bảng.

Trong trường hợp không thành công, chúng tôi sẽ không muốn chèn một hàng vào Orders bảng nhưng không có hàng tương ứng trong OrderItems bàn. Điều đó sẽ dẫn đến một đơn đặt hàng mà không có bất kỳ mục đặt hàng nào. Về cơ bản, chúng tôi muốn cả hai bảng được cập nhật hoàn toàn hoặc không có gì cả.

Nó cũng giống như vậy khi chúng tôi xóa các hàng. Chúng tôi muốn tất cả các hàng bị xóa hoặc không có hàng nào cả.

Trong SQL Server, chúng ta có thể viết giao dịch sau cho INSERT tuyên bố.

BEGIN TRANSACTION
    BEGIN TRY 
        INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
        VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');

        INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
        VALUES ( 5006, SYSDATETIME(), 1006 );
        
        INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
        VALUES ( 5006, 1, 1, 20, 25.99 );
        
        INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
        VALUES ( 5006, 2, 7, 120, 9.99 );

        COMMIT TRANSACTION;
        
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
    END CATCH

Ví dụ này giả định rằng có logic ở nơi khác xác định liệu khách hàng đã tồn tại trong cơ sở dữ liệu hay chưa.

Khách hàng có thể đã được chèn bên ngoài giao dịch này:


INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');

BEGIN TRANSACTION
    BEGIN TRY 

        INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
        VALUES ( 5006, SYSDATETIME(), 1006 );
        
        INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
        VALUES ( 5006, 1, 1, 20, 25.99 );
        
        INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
        VALUES ( 5006, 2, 7, 120, 9.99 );

        COMMIT TRANSACTION;
        
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
    END CATCH

Nếu giao dịch không thành công, khách hàng sẽ vẫn ở trong cơ sở dữ liệu (nhưng không có bất kỳ đơn đặt hàng nào). Ứng dụng sẽ cần kiểm tra xem khách hàng đã tồn tại hay chưa trước khi thực hiện giao dịch.

Giao dịch SQL với các điểm lưu

Điểm lưu xác định vị trí mà giao dịch có thể quay lại nếu một phần của giao dịch bị hủy có điều kiện. Trong SQL Server, chúng tôi chỉ định một điểm lưu với SAVE TRANSACTION savepoint_name (ở đâu savepoint_name là tên chúng tôi đặt cho điểm lưu).

Hãy viết lại ví dụ trước để bao gồm một điểm lưu:


BEGIN TRANSACTION
    INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
    VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
    SAVE TRANSACTION StartOrder;

    INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
    VALUES ( 5006, SYSDATETIME(), 1006 );
    
    INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
    VALUES ( 5006, 1, 1, 20, 25.99 );
    
    INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
    VALUES ( 5006, 2, 7, 120, 9.99 );
    ROLLBACK TRANSACTION StartOrder;
COMMIT TRANSACTION;
SELECT @@TRANCOUNT;

Tại đây, chúng tôi đã đặt điểm lưu ngay sau khi khách hàng INSERT tuyên bố. Sau đó trong giao dịch, tôi sử dụng ROLLBACK câu lệnh để hướng dẫn giao dịch quay trở lại điểm lưu đó.

Khi tôi chạy câu lệnh đó, khách hàng được chèn vào, nhưng không có thông tin đơn hàng nào được chèn vào.

Nếu một giao dịch được quay trở lại điểm lưu, nó phải tiếp tục hoàn thành với nhiều câu lệnh SQL hơn nếu cần và COMMIT TRANSACTION hoặc nó phải được hủy hoàn toàn bằng cách thu hồi toàn bộ giao dịch.

Nếu tôi di chuyển ROLLBACK câu lệnh quay lại INSERT trước đó tuyên bố, như thế này:

BEGIN TRANSACTION
    INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
    VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
    SAVE TRANSACTION StartOrder;

    INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
    VALUES ( 5006, SYSDATETIME(), 1006 );
    
    INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
    VALUES ( 5006, 1, 1, 20, 25.99 );
    ROLLBACK TRANSACTION StartOrder;
    
    INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
    VALUES ( 5006, 2, 7, 120, 9.99 );
COMMIT TRANSACTION;
SELECT @@TRANCOUNT;

Điều này tạo ra lỗi xung đột khóa ngoại. Cụ thể, tôi gặp lỗi sau:

(1 row affected)
(1 row affected)
(1 row affected)
Msg 547, Level 16, State 0, Line 13
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_OrderItems_Orders". The conflict occurred in database "KrankyKranes", table "dbo.Orders", column 'OrderId'.
The statement has been terminated.
(1 row affected)

Điều này xảy ra bởi vì, ngay cả khi đơn đặt hàng đã được chèn, thao tác đó đã được hoàn tác khi chúng tôi quay trở lại điểm lưu. Sau đó, giao dịch tiếp tục hoàn tất. Nhưng khi nó gặp phải mục đơn đặt hàng cuối cùng, không có đơn đặt hàng tương ứng (vì điều đó đã được hoàn tác) và chúng tôi đã gặp lỗi.

Khi tôi kiểm tra cơ sở dữ liệu, khách hàng đã được chèn vào, nhưng một lần nữa, không có thông tin đơn hàng nào được chèn.

Bạn có thể tham chiếu cùng một điểm lưu từ nhiều nơi trong giao dịch nếu cần.

Trong thực tế, bạn sẽ sử dụng lập trình có điều kiện để trả giao dịch về một savepont.

Giao dịch lồng nhau

Bạn cũng có thể lồng các giao dịch bên trong các giao dịch khác nếu được yêu cầu.

Như thế này:

BEGIN TRANSACTION Transaction1;  
    UPDATE table1 ...;
    BEGIN TRANSACTION Transaction2;
        UPDATE table2 ...;
        SELECT * from table1;
    COMMIT TRANSACTION Transaction2;
    UPDATE table3 ...;
COMMIT TRANSACTION Transaction1;

Như đã đề cập, cú pháp chính xác mà bạn sử dụng để tạo giao dịch sẽ phụ thuộc vào DBMS của bạn, vì vậy hãy kiểm tra tài liệu về DBMS để có bức tranh hoàn chỉnh về các tùy chọn của bạn khi tạo giao dịch trong SQL.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Sử dụng Geekbench 3 để đánh giá hiệu suất máy chủ cơ sở dữ liệu

  2. BẢNG ALTER SQL cho người mới bắt đầu

  3. Sử dụng các phiên bản được đặt tên? Kiểm tra kết nối DAC của bạn!

  4. Tốc độ thông lượng tuần tự và nguồn cấp dữ liệu

  5. Đếm các tham chiếu đến một bản ghi trong bảng thông qua Phím ngoại