CHƯƠNG III : LẬP TRèNH
3.5 GIAO TÁC TRONG SQL
Cho đến lỳc này, mụ hỡnh cỏc thao tỏc trờn cơ sở dữ liệu của chỳng ta là một người sử dụng đang truy vấn và sửa đổi cơ sở dữ liệu. Như vậy, cỏc thao tỏc trờn cơ sở dữ liệu được thực hiện một lần một thao tỏc, và trạng thỏi cơ sở dữ liệu để lại sau mỗi thao tỏc là trạng thỏi mà thao tỏc tiếp theo sẽ hành động trờn đú. Hơn nữa, chỳng ta hỡnh dung rằng cỏc thao tỏc được thực hiện trong trạng thỏi nguyờn vẹn của nú (“một cỏch nguyờn tử”). Như vậy, chỳng ta đó giả thiết rằng phần cứng và phần mềm khụng thể hỏng ở giữa thao tỏc, để lại cơ sở dữ liệu trong tỡnh trạng khụng thể giải thớch được như là kết quả của thao tỏc thực hiện trờn nú.
Cuộc sống thực thường là phức tạp hơn nhiều. Trước tiờn chỳng ta xem xột cỏi cú thể xảy ra làm cho cơ sở dữ liệu ở trạng thỏi khụng phản ỏnh cỏc thao tỏc thực hiện trờn nú và sau đú chỳng ta sẽ xem xột cỏc cụng cụ SQL cung cấp cho cỏc người sử dụng để đảm bảo rằng những vấn đề đú khụng xảy ra.
3.5.1 Xếp hàng theo thứ tự
Trong cỏc ứng dụng như là ngõn hàng hoặc đăng ký vộ mỏy bay, mỗi giõy cú hàng trăm thao tỏc cú thể thực hiện trờn cơ sở dữ liệu. Cỏc thao tỏc này
của một đại lý du lịch, nhõn viờn hàng khụng, …. Việc chỳng ta cú thể cú hai thao tỏc thực hiện trờn cựng một tài khoản hoặc một chuyến bay và chồng chộo thời gian là điều hoàn toàn cú thể xảy ra. Nếu như vậy, chỳng cú thể tương tỏc theo cỏc cỏch lạ lựng. Đõy là một vớ dụ về cỏi cú thể dẫn đến sai lầm nếu như hệ quản trị cơ sở dữ liệu khụng bị ràng buộc đầy đủ đối với thứ tự mà theo thứ tự đú nú thao tỏc trờn cơ sở dữ liệu. Chỳng ta nhấn mạnh rằng cỏc hệ cơ sở dữ liệu khụng xử sự một cỏch bỡnh thường theo cỏch này. Và rằng người ta cú thể đi ra theo cỏch của người ta để làm cho cỏc loại sai làm này xảy ra khi sử dụng một hệ quản trị thương mại.
1) EXEC SQL BEGIN DECLARE SECTION; 2) int flight ; /* số chuyến bay */
3) char date[10] ; /*ngày bay theo khuụn dạng SQL */
4) char seat[3] ; /* hai chữ số và một chữ cỏi biểu thị một chỗ ngồi */ 5) int occ ; /* biến logic để xem chỗ cú bị bận hay chưa */
6) EXEC SQL END DECLARE SECTION ; 7) void chooseSeat () {
8) /* Mó chương trỡnh C để nhắc người sử dụng nhập vào chuyến bay, ngày và chỗ ngồi và lưu giữ chỳng vào ba biến cú tờn ở trờn */
9) EXEC SQL UPDATE occupied INTO:occ 10) FROM Flights
11) WHERE fltNum =:flight AND fltDate =: date AND fltSeat =:seat;
12) if (!occ) {
13) EXEC SQL UPDATE Flights 14) SET occupied = TRUE
15) WHERE fltNum =: flight AND fltDate =:date AND fltSeat = seat ;
16) /* Mó chương trỡnh C để ghi lại việc phõn chỗ và thụng bỏo cho người sử dụng việc phõn chỗ */ }
17) else /* mó chương trỡnh C để để thụng bỏo cho người sử dụng việc chỗ đó bị chiếm và yờu cầu chọn chỗ khỏc */
}
Hỡnh 3.22: Chọn một chỗ
Vớ dụ 3.26: Giả sử rằng chỳng ta viết một hàm chooseSeat() trong C với SQL nhỳng để đọc một quan hệ về cỏc chuyến bay và cỏc chỗ sẵn cú, tỡm một chỗ sẵn cú cụ thể và làm cho nú trở thành bị chiếm. Quan hệ mà chỳng ta thao tỏc trờn đú được gọi là Flights và nú cú cỏc thuộc tớnh fltNum, ftlDate, ftlSeat, và occ với ý nghĩa rừ ràng.
Từ dũng (9) đến dũng (11) là một select đơn hàng và làm cho biến chia sẻ occ trở thành true hoặc false (1 hoặc 0) phụ thuộc vào việc chỗ được chỉ ra đó bị chiếm hay chưa. Dũng (12) kiểm tra xem chỗ đó bị chiếm hay chưa, và nếu chưa, bộ giỏ trị đối với chỗ này sẽ được cập nhật để làm cho nú trở thành bị chiếm. Việc cập nhật được thực hiện từ dũng (13) đến dũng (15) và tại dũng (16) chỗ được phõn cho người sử dụng yờu cầu nú. Trờn thực tế, chỳng ta cú thể lưu trữ thụng tin phõn chỗ vào một quan hệ khỏc. Cuối cựng, ở dũng (17), nếu chỗ đó bị chiếm thỡ người sử dụng cũng được thụng bỏo như vậy.
Bõy giờ hóy nhớ rằng hàm chooseSeat () cú thể được hai hay nhiều khỏch hàng thực hiện một cỏch đồng thời. Giả sử rằng hai đại lý đang cố gắng mua cựng một chỗ đối với cựng chuyến bay và cựng ngày tại cựng một thời điểm như gợi ý trong hỡnh 3.23. Cả hai đi đến dũng (9) cựng một lỳc và cả hai bản sao biến cục bộ occ nhận giỏ trị 0; như vậy, chỗ ngồi hiện đang cũn trống. Tại dũng (12), mỗi thực hiện của chooseSeat() quyết định sửa đổi occ thành TRUE và như vậy là làm cho chỗ thành bị chiếm. Cỏc cập nhật này thực hiện đồng thời và mỗi thực hiện núi với khỏnh hàng ở dũng (16) rằng chỗ thuộc về họ.
đắn: cả hai khỏch hàng tin rằng đó đăng ký được chỗ theo yờu cầu. Vấn đề cú thể được giải quyết bằng nhiều cơ cấu SQL phục vụ cho xếp hàng cú thứ tự sự thực hiện của hai thực hiện hàm. Chỳng ta nối về cỏc hàm thao tỏc trờn cựng một cơ sở dữ liệu là cú thứ tự nếu một một hàm thực hiện hoàn thành trước khi một hàm khỏc bắt đầu. Chỳng ta núi thực hiện là làm cú thứ tự được nếu chỳng chỳng xử sự như là chỳng đó chạy một cỏch cú thứ tự dự rằng việc thực hiện của chỳng là cú thời gian gối lờn nhau.
Rừ ràng là nếu hai lời gọi hàm chooSeat() chạy cú thứ tự thỡ sai sút mà chỳng ta đó nhỡn thấy khụng thể xảy ra. Một lời gọi của khỏch hàng xuất hiện đầu tiờn. Khỏch hàng này nhỡn thấy một chỗ trống và mua chỗ đú. Sau đú lời gọi của khỏch hàng thứ hai bắt đầu và nhỡn thấy chỗ đú đó bị chiếm. Nú cú thể quan trọng đối với những khỏch hàng đăng ký chỗ nhưng đối với cơ sở dữ liệu, điều quan trọng là một chỗ chỉ được bỏn một lần.
3.5.2 Atomicity
Bờn cạnh cỏch xử sự khụng cú thứ tự cú thể xảy ra nếu cú hai hoặc nhiều phộp toỏn cơ sở dữ liệu thực hiện cựng một thời điểm, cũn cú thể cú khả năng một phộp toỏn đơn đặt cơ sở dữ liệu vào một trạng thỏi khụng chấp nhận được nếu cú một sự sụp đổ phần cứng hoặc phần mềm khi phộp toỏn đang thực hiện. Đõy là một vớ dụ khỏc gợi ý cỏi gỡ cú thể xảy ra. Cũng như trong vớ dụ 8.26, chỳng ta phải nhớ rằng cỏc hệ cơ sở dữ liệu thực sự khụng cho phộp kiểu sai sút này xảy ra trong cỏc chương trỡnh ứng dụng được thiết kế đỳng đắn.
Vớ dụ 3.27: Chỳng ta xột một loại cơ sở dữ liệu khỏc: cỏc bản ghi tài khoản của ngõn hàng. Chỳng ta cú thể trỡnh bày tỡnh huống bằng một quan hệ Accounts với cỏc thuộc tớnh accNo và balance. Cặp trong quan hệ này là mó số tài khoản và số dư trong tài khoản đú.
Chỳng ta muốn viết một hàm transfer() đưa vào hai tài khoản và một số tiền, kiểm tra xem tài khoản thứ nhất cú ớt nhất là số tiền đú hay khụng và nếu cú thỡ chuyển tiền từ tài khoản thứ nhất sang tài khoản thứ hai. Hỡnh 8.24 là phỏc thảo của hàm transfer():
2) int acct, acct2 ; /* hai tài khoản */
3) int balance1 ; /* số tiền trong tài khoản thứ nhất */ 4) int amount ; /* số tiền cần chuyển */
5) EXEC SQL END DECLARE SECTION; 6) void transfer() {
7) /* Mó C nhắc người sử dụng nhập vào cỏc tài khoản 1 và 2 và một số tiền cần chuyển, vào cỏc biến acct1, acct2, và amount. */
8) EXEC SQL SELECT balance INTO:balance1 9) FROM Accounts
10) WHERE acctNo =:acct1 ; 11) if (balance1 >= amount) {
12) EXEC SQL UPDATE Accounts 13) SET balance = balance +:amount 14) WHERE acctNo =:acct2 ;
15) EXEC SQL UPDATE Accounts 16) SET balance = balance ư:amount 17) WHERE acctNo =:acct1 ;
}
18) else /* Mó C để in thụng bỏo rằng khụng cú đủ tiền để chuyển */ Hỡnh 3.24 Chuyển tiền từ tài khoản này sang tài khoản khỏc.
Cụng việc của hỡnh 3.24 là kế tiếp. Cỏc dũng (8) đến dũng (10) lấy ra số dư của tài khoản thứ nhất. Ở dũng (11), kiểm tra xem số dư đú cú đủ để rỳt số tiền mong muốn ra hay khụng. Nếu đủ, thỡ cỏc dũng từ (12) đến (14) thờm số tiền đú vào tài khoản thứ hai và cỏc dũng từ (15) đến (17) trừ số tiền đú ra khỏi tài khoản thứ nhất. Nếu số tiền trong tài khoản thứ nhất là khụng đủ thỡ khụng cú sự chuyển tiền xảy ra và một lời cảnh bỏo sẽ được in ra ở dũng
Bõy giờ chỳng ta xem cỏi gỡ xảy ra nếu cú một hư hỏng sau dũng (14). Cú thể đú là một sự hư hỏng của mỏy tớnh hoặc mạng nối cơ sở dữ liệu với bộ xử lý đang thực hiện việc chuyển tiền hỏng. Khi đú cơ sở dữ liệu được đặt ở trạng thỏi tiền đó được chuyển vào tài khoản thứ hai nhưng chưa được trừ đi khỏi tài khoản thứ nhất. Kết quả là ngõn hàng mất số tiền đó được chuyển đi. Vấn đề được minh họa bởi vớ dụ 3.27 là một tổ hợp cỏc phộp toỏn cơ sở dữ liệu, như là hai phộp update ở hỡnh 3.24, cần được thực hiện một cỏch nguyờn tử, nghĩa là cả hai đều được thực hiện hoặc khụng phộp toỏn nào được thực hiện. Vớ dụ, một lời giải đơn giản làm tất cả cỏc thay đổi trong một nơi cục bộ và chỉ sau khi tất cả cụng việc được thực hiện chỳng ta sẽ ghi cỏc thay đổi vào cơ sở dữ liệu, và sau đú tất cả cỏc thay đổi trở thành một phần của cơ sở dữ liệu và nhỡn thấy được đối với cỏc phộp toỏn khỏc.
3.5.3 Giao tỏc (Transaction)
Cỏch giải quyết cỏc vấn đề về xếp hàng thứ tự và nguyờn tử húa đặt ra trong cỏc phần 3.6.1 và 3.6.2 là nhúm cỏc phộp toỏn vào cỏc giao tỏc. Một giao tỏc là một tập hợp của một hoặc nhiều phộp toỏn trờn cơ sở dữ liệu sao cho chỳng được thực hiện một cỏch nguyờn tử; nghĩa là hoặc tất cả cỏc phộp toỏn được thực hiện hoặc khụng phộp toỏn nào được thực hiện. Hơn nữa, SQL đũi hỏi rằng, như ngầm định, cỏc giao tỏc được thực hiện theo cỏch xếp hàng thứ tự. Một hệ quản trị cơ sở dữ liệu cú thể cho phộp người sử dụng chỉ ra một ràng buộc ớt nghiờm ngặt hơn trờn việc chốn của cỏc phộp toỏn từ hai hoặc nhiều giao tỏc. Chỳng ta sẽ thảo luận những sửa đổi đối với điều kiện xếp hàng thứ tự này trong cỏc phần sau. Khi sử dụng giao diện SQL chung, mỗi một lệnh chớnh là một giao tỏc. Tuy nhiờn, Khi viết chương trỡnh với SQL nhỳng hoặc chương trỡnh sử dụng SQL/CLI hoặc JDBC, chỳng ta thường muốn kiểm tra cỏc giao tỏc một cỏch rừ ràng. Cỏc giao tỏc bắt đầu một cỏch tự động khi một lệnh SQL bắt đầu truy vấn hoặc thao tỏc cơ sở dữ liệu hoặc lược đồ. Nếu muốn chỳng ta cú thể sử dụng lệnh SQL START TRANSACTION.
Trong giao diện chung, trừ phi được bắt đầu bằng lệnh START TRANSACTION giao tỏc kết thỳc cựng với lệnh. Trong cỏc trường hợp
1. Lệnh SQL COMMIT khiến giao tỏc kết thỳc một cỏch thành cụng. Bất cứ cỏi gỡ làm thay đổi đối với cơ sở dữ liệu được gõy ra do một lệnh hoặc cỏc lệnh SQL từ khi giao tỏc hiện tại bắt đầu sẽ được đặt thường trực vào cơ sở dữ liệu (tức là chỳng được giữ lại). Trước khi lệnh COMMIT được thực hiện, cỏc thay đổi là khụng dứt khoỏt và cú thể hoặc khụng thể nhỡn thấy được đối với cỏc giao tỏc khỏc.
2. Lệnh SQL ROLLBACK khiến giao tỏc kết thỳc khụng thành cụng (hoặc bỏ dở). Mọi thay đổi trong việc trả lời cho cỏc lệnh SQL của giao tỏc là khụng thực hiện (tức là chỳng được rolled back), vỡ vậy chỳng khụng cũn xuất hiện trong cơ sở dữ liệu.
Cú một ngoại lệ đối với hai điểm trờn. Nếu chỳng ta cố gắng lưu giữ một giao tỏc nhưng cú cỏc ràng buộc chậm và cỏc ràng buộc đú bõy giờ bị vi phạm thỡ giao tỏc khụng được ghi lại dự chỳng ta núi với nú bằng lệnh COMMIT. Hơn nữa, giao tỏc sẽ bị bỏ dở và một chỉ dẫn trong SQLSTATE núi với ứng dụng rằng giao tỏc bị bỏ dở do nguyờn nhõn này.
Vớ dụ 3.28: Giả sử chỳng ta muốn một thực hiện của hàm transfer() của hỡnh 8.24 là một giao tỏc đơn. Giao tỏc bắt đầu ở dũng (8) khi chỳng ta đọc số dư của tài khoản thứ nhất. Nếu kiểm tra ở dũng (11) là đỳng và chỳng ta thực hiện việc chuyển tiền thỡ chỳng ta cú thể muốn ghi lại cỏc thay đổi đó được làm. Như vậy, chỳng ta đặt ở cuối khối if từ dũng (12) đến dũng (17) thờm lệnh SQL
EXEC SQL COMMIT ;
Nếu kiểm tra ở dũng (11) là sai – nghĩa là khụng cú đủ tiền để thực hiện việc chuyển – thỡ chỳng ta cú thể bỏ dở việc chuyển. Chỳng ta cú thể làm như vậy bằng cỏch đặt EXEC SQL ROLLBACK ở cuối khối else ở dũng (18). Hiện tại, bởi vỡ trong nhỏnh này khụng cú lệnh sửa đổi cơ sở dữ liệu nào được thực hiện, chỳng ta lưu giữ (commit) hoặc bỏ dở (abort) cũng khụng vấn đề gỡ bởi vỡ khụng cú thay đổi nào được lưu giữ cả.
3.5.4 Read-Only Transaction
hàng thứ tự. Như chỳng ta đó nhỡn thấy trong vớ dụ 3.26 cỏi gỡ cú thể xảy ra nếu hai thực hiện của hàm cố gắng mua cựng một chỗ tại cựng một thời điểm, và chỳng ta cũng đó nhỡn thấy trong vớ dụ 3.27 cỏi gỡ cú thể xảy ra nếu cú một sự hỏng húc trong thời gian thực hiện hàm. Tuy nhiờn, khi một giao tỏc chỉ đọc dữ liệu và khụng ghi dữ liệu chỳng ta sẽ tự do hơn trong việc cho một giao tỏc thực hiện song song với cỏc giao tỏc khỏc.
Vớ dụ 3.29: Giả sử chỳng ta đó viết một hàm đọc cỏc dữ liệu để xỏc định xem một chỗ nào đú cú sẵn sàng hay khụng, hàm này sẽ xử sự giống như dũng (1) đến dũng (11) của hỡnh 8.22. Chỳng ta cú thể thực hiện nhiều lần gọi hàm này cựng một lỳc mà khụng sợ làm hại đến cơ sở dữ liệu. Điều tệ hại nhất cú thể xảy ra là khi chỳng ta đang đọc về sự sẵn sàng của một chỗ nào đú thỡ chỗ đú cú thể đó bị mua hoặc được giải phúng bởi sự thực hiện của một hàm khỏc nào đú. Như vậy, chỳng ta cú thể nhận được cõu trả lời “sẵn sàng” hoặc “bị chiếm” trong khoản thời gian rất ngắn khi ta thực hiện truy vấn nhưng cõu trả lời sẽ cú nghĩa trong một lỳc.
Nếu chỳng ta bảo với hệ thống thực hiện SQL rằng giao tỏc hiện tại của chỳng ta là readưonly nghĩa là chỳng sẽ chẳng bao giờ làm thay đổi cơ sở dữ liệu thỡ hệ thống SQL hoàn toàn cú thể cú khả năng nhận ưu điểm của kiến thức đú. Núi chung, việc nhiều giao tỏc readưonly truy cập đến cựng một dữ liệu để chạy song song là cú thể được, trong khi đú chỳng sẽ khụng cho phộp chạy song song với một giao tỏc ghi cựng một dữ liệu.
Chỳng ta bỏo cho hệ thống SQL rằng giao tỏc tiếp theo sau là readưonly bằng lệnh
SET TRANSACTION READ ONLY ;
Lệnh này phải được thực hiện trước khi giao tỏc bắt đầu. Vớ dụ, nếu chỳng ta cú một hàm bao gồm cỏc dũng từ (1) đến dũng (11) của hỡnh 3.22, chỳng ta cú thể khai bỏo nú là read only bằng cỏch đặt
EXEC SQL SET TRANSACTION READ ONLY ;
ngay trước dũng (9), nơi bắt đầu giao tỏc. Nếu khai bỏo readưonly sau dũng (9) thỡ sẽ quỏ muộn.
Chỳng ta cũng cú thể thụng bỏo cho SQL rằng giao tỏc sắp tới cú thể ghi dữ