You are on page 1of 16

Các Cơ Chế Thực Thi Lệnh JOIN

Vũ Huy Tâm Tiếp theo bài Các Loại JOIN Trong SQL Server, bài này giới thiệu về các cơ chế bên trong SQL Server sử dụng để xử lý các câu truy vấn JOIN. Về cơ bản khi thực hiện câu lệnh JOIN, SQL Server duyệt qua hai bảng tham gia vào, lấy ra từng cặp bản ghi để so sánh, rồi trả về tập kết quả nếu thỏa mãn điều kiện JOIN hoặc loại bỏ nếu không thỏa mãn. SQL Se rver cài đặt một vài thuật toán khác nhau, thích hợp với các tình huống khác nhau (như số lượng bản ghi cần so sánh nhiều hay ít, cột JOIN có index hay không…). Các thuật toán đó là Nested Loop Join, Merge Join, và Hash Join.

Nested Loop Join
Đây là thuật toán rất đơn giản và cũng rất hiệu quả đối với tập dữ liệu nhỏ, nó lấy mỗi bản ghi trong một bảng (gọi là inner table) và so sánh với từng bản ghi của bảng kia (gọi là outer table) để tìm ra bản ghi thỏa mãn. Thuật toán này có thể được viết ở dạng pseodo code như sau: for each row R1 in the outer table for each row R2 in the inner table if R1.join_column = R2.join_column return (R1, R2) (pseodo-code được copy từ Craig Freedman‟s Blog) ví dụ 1:
USE AdventureWorkds GO SELECT a.SalesOrderDetailID, b.Name FROM Sales.SalesOrderDetail a JOIN Production.Product b ON a.ProductID = b.ProductID WHERE a.SalesOrderID = 43659

vì lúc đó các thuật toán này có thể sẽ hiệu quả hơn so với Nested Loop Join. bộ Optimizer đã chọn Nested Loop Join. Nói chung Nested Loop Join thích hợp khi outer table có số bản ghi nhỏ và inner table đã được sắp xếp theo trường được JOIN (ví dụ trường này có clustered index). bạn thấy hai thao tác Clustered index seek. bộ Optimizer sẽ tự động chọn một bảng làm outer table và bảng kia làm inner table theo cách để đảm bảo chi phí là nhỏ nhất. bảng ở thao tác trên luôn luôn là outer table. Nested Loop Join là thao tác join bạn mong đợi xuất hiện nhất trong phương án thực thi. việc quét bảng inner table (vòng lặp trong) trở thành index seek. bộ Optimizer sẽ xem xét đến Merge Join hoặc Hash Join. Theo thuật ngữ của Microsoft. Vì số bản ghi được trả về là nhỏ. vì nó chứng tỏ câu truy vấn được thực hiện hiệu quả và có chi phí thấp. Khi số bản ghi tăng lên hoặc bảng inner table không được sắp thứ tự (vì không có index hỗ trợ). Cần phân biệt các thuật toán kể trên với các loại JOIN mà bạn dùng khi viết code như INNER JOIN. Khi đó. còn các thuật toán trên gọi là physical operator (các toán tử ở mức vật lý). và bảng dưới luôn là inner table. hệ thống sẽ xem xét và sử dụng một toán tử mức vật lý (một trong ba thuật toán) thích hợp để thực thi câu lệnh. Khi viết code. bạn dùng các toán tử mức logic để diễn đạt yêu cầu. OUTER JOIN. . Khi xây dựng phương án thực thi. các loại JOIN đó được gọi là logical operator (các toán tử ở mức logic).Trong phương án thực thi ở hình trên.

b. Với thuật toán này.ProductID --WHERE a.join_column < R2.join_column get next row R1 from table 1 else get next row R2 from table 2 end (pseodo-code được copy từ Craig Freedman‟s Blog ) Ví dụ 2: Cùng câu truy vấn như ở phần trước.SalesOrderDetailID. Nếu khớp thì gửi ra tập kết quả.ProductID = b. Nó đọc từng cặp bản ghi của mỗi bảng và so sánh với nhau.Merge Join Kỹ thuật này đòi hỏi hai bảng phải cùng được sắp xếp theo thứ tự của trường JOIN.SalesOrderDetail a JOIN Production. hai bảng được đọc từ đầu và cùng tiến lên song song với nhau. R2) get next row R2 from table 2 end else if R1. Pseodo-code của thuật toán Merge Join như sau: get first row R1 from table 1 get first row R2 from table 2 while not at the end of either table begin if R1. Nếu không thì nó loại bản ghi có trường JOIN nhỏ hơn.join_column = R2.Name FROM Sales. đọc tới bản ghi tiếp theo của bảng tương ứng và tiếp tục quá trình.Product b ON a.join_column begin return (R1. nhưng bỏ qua mệnh đề WHERE SELECT a.SalesOrderID = 43659 .

và do đó. rồi dùng giá trị băm đó để tìm trên bảng băm. rồi xây dựng một bảng băm (hash table) trong bộ nhớ. bộ Optimizer có hai lựa chọn: (1) sắp xếp lại bảng theo thứ tự trường JOIN rồi áp dụng Merge Join hoặc (2) chuyển sang dùng Hash Join. Đến bước dò tìm. và tất nhiên index đã sắp xếp sẵn). Pseodo-code: . nó đọc bảng thứ hai (gọi là probe table) và cũng băm các bản ghi dùng trường JOIN. Nếu một trong hai bảng không được sắp sẵn thứ tự. và băm (hash) các bản ghi dựa vào trường JOIN. Hash Join Thuật toán này phát huy hiệu quả nhất đối với lượng dữ liệu lớn và không được sắp xếp sẵn. vì số bản ghi trả về là lớn và cả hai bảng đều đã được sắp xếp (nói chính xác ra là. nó quét qua một bảng (gọi là build table). Khi đó số lần so sánh chỉ bằng hai lần số bản ghi của bảng nhỏ. đối với bảng SalesOrderDetail nó chỉ cần đọc index trên trường ProductID. Nó được thực hiện làm hai giai đoạn: xây dựng (build) và dò tìm (probe). Ở bước xây dựng. Trong nhiều trường hợp. Do đó thuật toán này hoạt động hiệu quả hơn Nested Loop khi số bản ghi tăng cao. chi phí của thuật toán này.Với câu lệnh trên thì Merge Join trở nên thích hợp. tương đương với tổng của số bản ghi trong hai bảng. thuật toán kết thúc khi nó mới chỉ quét xong bảng nhỏ hơn. Phương án nào rẻ hơn sẽ được chọn. vì bảng kia nếu có quét tiếp cũng không tìm được bản ghi nào thỏa mãn nữa. Số phép so sánh. Mỗi lần tìm được nó gửi cặp bản ghi tương ứng ra tập kết quả.

join_column = R2. b. vì có thêm trường OrderQty nên chỉ đọc index trên trường ProductID là không đủ mà hệ thống . bảng SalesOrderDetail chỉ cần đọc index trên trường ProductID là đủ. Lưu ý ở ví dụ 2.join_column for each row R1 in the corresponding hash bucket if R1. a.SalesOrderDetailID.join_column return (R1.OrderQty. và vì input cho việc join đã được sắp xêp nên Merge Join đã được dùng. mặc dù số bản ghi được trả về giống như ở ví dụ 2.join_column insert R1 into the appropriate hash bucket end for each row R2 in the probe table begin calculate hash value on R2.for each row R1 in the build table begin calculate hash value on R1.ProductID = b. Nhưng ở ví dụ 3.ProductID --WHERE a.SalesOrderDetail a JOIN Production.Product b ON a. R2) end (pseodo-code được copy từ Craig Freedman‟s Blog ) Ví dụ 3: giống như ví dụ 2 nhưng thêm một trường OrderQty vào phần SELECT SELECT a.Name FROM Sales.SalesOrderID = 43659 Ở ví dụ này Hash Join đã được sử dụng.

chủ ý hoặc do quên không đưa vào) và không có index hỗ trợ. Và giờ câu lệnh đã được thực hiện bằng Merge Join và hiệu năng đã được cải thiện đáng kể: --Tạo một bảng copy của Sales.tạo các index trên bảng copy . Khi Hash Join xuất hiện trong phương án thực thi. hơn nữa nó chỉ cần áp dụng đối với một bảng. khi quan sát kế hoạch thực thi và thấy Hash Join được sử dụng bạn hiểu rằng đây có thể là chỉ dấu câu lệnh chưa được thực hiện tối ưu. Thông thường bộ Optimizer chọn bảng nhỏ để xây dựng bảng băm để không cần chiếm quá nhiều bộ nhớ. Trong các hệ thống OLTP. Tuy vậy so với Nested Loop Join và Merge Join thì thuật toán này đòi hỏi rất nhiều tài nguyên CPU và bộ nhớ. đó là chỉ dấu cho thấy lượng dữ liệu cần xử lý khá lớn (do không có mệnh đề WHERE. Hash Join cho thấy nhiều khả năng là câu lệnh chưa được thực hiện tối ưu.SalesOrderDetail GO -. vốn đặc trưng bởi nhiều giao dịch được thực hiện nhanh. Để dùng Merge Join thì đầu vào phải được sắp xếp. các thao tác xử lý thường trên một lượng dữ liệu lớn. Input cho thao tác join lúc này không còn được sắp xếp nữa và do đó. Ví dụ. Bạn cố gắng tạo thay đổi để hệ thống chuyển sang chọn Merge Join (vì số bản ghi trả về lớn nên Nested Loop Join chắc chắn không thích hợp).SalesOrderDetail SELECT * INTO Sales. Việc hiểu biết cơ chế hoạt động của các thuật toán giúp bạn có thêm một công cụ để tối ưu hóa câu lệnh. do đó Hash Join được sử dụng rất thường xuyên. Trong các tình huống như thế này Hash Join có ưu thế hơn Merge Join vì việc xây dựng bảng băm nhanh hơn sắp xếp lại bảng. Hash Join trở nên thích hợp hơn. Tuy nhiên mức sâu nhất mà bạn có thể nhìn vào hệ thống là biết thuật toán nào đã được sử dụng cho câu lệnh. Vì thế bạn có thể sửa lại index trên trường ProductID để nó cover cả trường OrderQty.SalesOrderDetail_2 FROM Sales. Lời kết Trên đây giới thiệu các thuật toán SQL Server dùng để thực thi câu lệnh JOIN. Còn trong môi trường data warehouse. Trên thực tế các thuật toán phức tạp hơn và còn có nhiều biến thể để bộ Optimizer tinh chỉnh trong từng tình huống cụ thể. do Microsoft che dấu toàn bộ các chi tiết bên dưới.phải đọc cả vào bảng nữa. với câu truy vấn ở phần Hash Join.

b.Query 1: câu lệnh trên bảng cũ SELECT a.SalesOrderDetail_2(ProductID) INCLUDE(OrderQty) -.OrderQty.so sánh hai câu lệnh: -.SalesOrderDetailID.Product b ON a. SalesOrderDetailID) GO CREATE NONCLUSTERED INDEX IX_SalesOrderDetail_ProductID_2 ON Sales.SalesOrderDetailID. b.ProductID .Name FROM Sales.Name FROM Sales.Query 2: câu lệnh trên bảng copy SELECT a.OrderQty.SalesOrderDetail_2 ( SalesOrderID.ProductID = b.CREATE CLUSTERED INDEX IX_SalesOrderDetail_SalesOrderID_SalesOrderDetailID_2 ON Sales.Product b ON a.ProductID = b.SalesOrderDetail_2 a JOIN Production.ProductID -.SalesOrderDetail a JOIN Production. a. a.

.Update Lock = Intent-to-update Lock . delete) trên 1 đơn vị dữ liệu. 3. update.1.Không thể thiết lập Exclusive Lock trên đơn vị dữ liệu đang có Shared Lock.Shared Lock ( Read Lock ): Khi đọc 1 đơn vị dữ liệu. . 1 khóa hay trên 1 dòng dữ liệu. Shared Lock Update Lock Tương thích với Shared Lock Tương thích với Shared Lock Sử dụng trong việc đọc dữ liệu Sử dụng trong việc đọc dữ liệu Tại 1 thời điểm có thể có nhiều Tại 1 thời điểm. SQL Server tự động thiết lập Shared Lock trên đơn vị dữ liệu đó (trừ trường hợp sử dụng No Lock) . 1 trang.Tại 1 thời điểm.Nhiều giao tác có thể đồng thời giữ Shared Lock trên cùng 1 đơn vị dữ liệu. chỉ có tối đa 1 giao tác được quyền giữ Exclusive Lock trên 1 đơn vị dữ liệu.Shared Lock có thể được thiết lập trên 1 bảng.Shared Lock thường được giải phóng ngay sau khi sử dụng xong dữ liệu được đọc.Update Lock :không ngăn cản việc thiết lập các Shared Lock khác trên cùng 1 đơn vị dữ liệu => Update Lock tương thích với Shared Lock .Update Lock :Giúp tránh hiện tượng deadlock khi có yêu cầu chuyển từ Shared Lock lên Exclusive Lock trên 1 đơn vị dữ liệu nào đó (Do tại 1 thời điểm chỉ có tối đa 1 Update Lock trên 1 đơn vị dữ liệu) Tóm lại : Ta có bảng tương thích giữa các loại khóa như sau : ( hai loại khóa x. Shared Locks (S) . . . . .Exclusive Lock luôn được giữ đến hết giao tác.Không thể thiết lập Exclusive Lock trên đơn vị dữ liệu đang có Shared Lock.Update Lock là chế độ khóa trung gian giữa Shared Lock và Exclusive Lock. trừ khi có thiết lập giữ shared lock cho đến hết giao tác.y được gọi là tương thích nếu như tại một thời điềm có thể có hai transaction đồng thời giữ 2 loại lock này trên đơn vị dữ liệu ) Tiêu đề ô 1 Shared lock Shared lock + Updlock + Exclusive Lock - .Update Lock sử dụng khi đọc dữ liệu với dự định ghi trở lại sau khi đọc trên đơn vị dữ liệu này.Exclusive Lock Ù Write Lock . SQL Server tự động thiết lập Exclusive Lock trên đơn vị dữ liệu đó. . 2. Update Locks (U) .Khi thực hiện thao tác ghi (insert. có tối đa 1 Shared Lock trên cùng1 đơn vị dữ Update Lock trên 1 đơn vị dữ liệu liệu . Exclusive Locks (X) .

Updlock Exclusive Lock + - - - .

NAME VARCHAR(50)) INSERT INTO dbo. Khi đó transaction thứ hai nhận được dữ liệu sai.'a' INSERT INTO dbo. vậy các transaction hoặc truy vấn khác xảy ra đồng thời và cùng tác động vào các bản ghi đó sẽ diễn ra thế nào? Chúng sẽ phải đợi đến khi transaction đầu hoàn thành hay có thể thực hiện song song.Item Nay bạn hãy mở hai cửa sổ trong Management Studio. Hãy tìm hiểu qua ví dụ sau: CREATE TABLE dbo. Từ bản 2005 bắt đầu bổ sung thêm một loại mới là Snapshot. kết quả dữ liệu nhận được là trong khi hay sau khi cập nhật? Bạn có thể điều khiển những hành vi này thông qua việc đặt isolation level của từng transaction.Item SET name = 'x' WHERE id>2 WAITFOR DELAY '00:00:10' --wait for 10 seconds ROLLBACK . Repeatable Read. một phần dữ liệu sẽ bị thay đổi (ví dụ một số bản ghi của bảng được sửa đổi hoặc bị xóa bỏ.Các Mức Isolation Level Vũ Huy Tâm Isolation level là một thuộc tính của transaction. Read Uncommitted Khi transaction thực hiện ở mức này. SQL Server cung cấp các mức isolation level sau xếp theo thứ tự tăng dần của mức độ cô lập của dữ liệu: Read Uncommitted.Item SELECT 3. qui định mức độ cô lập của dữ liệu mà transaction có thể truy nhập vào khi dữ liệu đó đang được cập bởi một transaction khác. và Serializable.Item SELECT 1.'b' INSERT INTO dbo. dữ liệu sẽ trở lại giá trị cũ. Khi một transaction cập nhật dữ liệu đang diễn ra. 1. ở cửa số thứ nhất bạn nhập vào: BEGIN TRAN UPDATE dbo.Item SELECT 2. Read Commited. Nếu vì lý do nào đó transaction ban đầu rollback lại những cập nhật.Item (id INT. Phần còn lại của bài này sẽ đi vào chi tiết của từng loại. các truy vấn vẫn có thể truy nhập vào các bản ghi đang được cập nhật bởi một transaction khác và nhận được dữ liệu tại thời điểm đó mặc dù dữ liệu đó chưa được commit (uncommited data). một số được thêm mới).'c' SELECT * FROM dbo.

Giờ hãy sửa lại đoạn lệnh ở cửa số thứ hai thành: SET TRANSACTION ISOLATION LEVEL READ COMMITTED SELECT * FROM dbo.Item WITH (NOLOCK) 2. Vì thế nó tránh được dirty read như ở mức trên. Read Commited Đây là mức isolation mặc định. Giờ hãy sửa lại code ở hai cửa sổ thành: . bản ghi số 3 lại trở lại giá trị ban đầu name=‟c‟. Chú ý là mức isolation này tương được với gợi ý “NOLOCK” khi truy vấn bảng. nếu việc đọc sai như trên là không thể chấp nhận được bạn cần đặt mức isolation cao hơn. Như vậy là transaction ở cửa số thứ hai đã nhận được dữ liệu sai vì dữ liệu này chưa được commit. Tuy nhiên sau đó transaction ở cửa số thứ nhất bị rollback và sau khi cả hai transaction kết thúc. hãy cho tôi dữ liệu hiện có ngay tại thời điểm này”.Item Giờ bạn thực hiện đoạn lệnh ở cửa sổ thứ nhất rồi nhanh chóng chuyển sang thực hiện đoạn lệnh ở cửa sổ thứ hai. hay còn gọi là dirty read. Tuy nhiên nếu transaction thứ hai insert thêm bản ghi nằm trong phạm vi cập nhật của transaction thứ nhất. Hiện tượng này gọi là uncommited read.Và ở cửa số thứ hai bạn nhập: SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED SELECT * FROM dbo. đoạn lệnh ở cửa sổ thứ hai tương đương với: SELECT * FROM dbo. Nói nôm na là yêu cầu đọc của nó là “tôi không cần biết dữ liệu có đang được cập nhật hay không.Item WHERE id>2 Và thực hiện lại hai cửa sổ theo trình tự như trên. nó vẫn được phép làm như vậy và gây nhiễu đến transaction cửa số 1 BEGIN TRAN thứ nhất. Ưu điểm của mức isolation này là tăng độ tương tranh trong database. các tiến trình đọc không cần đợi đến khi tiến trình ghi hoàn tất mà có thể lấy dữ liệu ra được ngay. bạn sẽ thấy cửa sổ thứ hai không trả về kết quả ngay mà phải đợi đến khi cửa số thứ nhất thực hiện xong. Tùy theo ứng dụng của bạn mà bạn có thể đặt mức isolation này không. Còn nếu có thể dung thứ được thì đặt mức này sẽ giúp tăng hiệu năng đọc cho hệ thống. Transaction sẽ không đọc được dữ liệu đang được cập nhật mà phải đợi đến khi việc cập nhật thực hiện xong. Và lần này cửa sổ thứ hai trả về dữ liệu đúng. nếu bạn không đặt gì cả thì transaction sẽ hoạt động ở mức này. Bạn sẽ thấy cửa số thứ hai trả về bản ghi số 3 với name = „x‟.

Tuy nhiên nó không bảo vệ được dữ liệu khỏi insert hoặc delete: nếu bạn thay lệnh update ở cửa sổ thứ hai bằng lệnh .Item WHERE id>2 Cửa sổ hai: SET TRANSACTION ISOLATION LEVEL READ COMMITTED INSERT INTO dbo. hai lệnh select ở cửa sổ 1 cho cùng kết quả và cửa sổ 2 phải đợi đến khi cửa sổ 1 hoàn tất mới được thực hiện.Item SET name = 'x' WHERE id>2 SELECT * FROM item Khi thực hiện code ở hai cửa sổ liên tiếp nhau.Item SET name = 'x' WHERE id>2 WAITFOR DELAY '00:00:10' --wait for 10 seconds --ROLLBACK COMMIT SELECT * FROM dbo.Item SELECT 5. Điều này hoàn toàn bất ngờ vì theo trình tự thực hiện đoạn lệnh ở cửa sổ thứ nhất. Mức isolation này đảm bảo các lệnh đọc trong cùng một transaction cho cùng kết quả. bản ghi 5 đã xuất hiện sau khi bảng được cập nhật nhưng trước khi transaction kết thúc. Vì thế nó được gọi là bản ghi ma (phantom row). Trong tình huống trên. Trở lại hai cửa sổ: Cửa số 1: BEGIN TRAN SELECT * FROM dbo. nói cách khác dữ liệu đang được đọc sẽ được bảo vệ khỏi cập nhật bởi các transaction khác.'e' Sau khi thực hiện cả hai cửa sổ bạn sẽ thấy kết quả trả về có chứa bản ghi 5 với name = „e‟.Item COMMIT Cửa sổ 2: SET TRANSACTION ISOLATION LEVEL REPEATABLE READ UPDATE dbo. 3. Repeatable read Mức isolation này hoạt động nhứ mức read commit nhưng nâng thêm một nấc nữa bằng cách ngăn không cho transaction ghi vào dữ liệu đang được đọc bởi một transaction khác cho đến khi transaction khác đó hoàn tất.UPDATE dbo. tất cả các bản ghi với id>2 đều được cập nhật.Item WAITFOR DELAY '00:00:10' --wait for 10 seconds SELECT * FROM dbo.

Khi càng lên mức cao. Tác dụng của nó là giảm blocking giữa các transaction mà vẫn đảm bảo tính toàn vẹn dữ liệu. Để thiết lập isolation mức này bạn cần đặt lại option của database: ALTER DATABASE TestDB SET ALLOW_SNAPSHOT_ISOLATION ON Về phạm vi áp dụng các mức isolation Các mức isolation từ 1 – 4 kể trên tăng theo thứ tự mức độ cô lập dữ liệu. Đồng thời nó cũng tăng thời gian chờ lẫ n nhau của các transaction. 5. nó không khóa các bản ghi này lại mà tạo một bản sao (snapshot) và select trên đó. Vì vậy các transaction khác insert/update lên các bản ghi đó không gây ảnh hưởng đến transaction ban đầu. dù là UPDATE/DELETE bản ghi đã có hay INSERT bản ghi mới.'d' Cửa sổ 2 sẽ bị treo đến khi cửa sổ 1 thực hiện xong.insert. Serializable Mức isolation này tăng thêm một cấp nữa và khóa toàn bộ dải các bản ghi có thể bị ảnh hưởng bởi một transaction khác. và hai lệnh SELECT trong cửa sổ 1 trả về kết quả giống nhau. Vì thế nó vẫn không tránh được hiện tượng bản ghi ma. giúp tăng tính toàn vẹn dữ liệu và nhất quán của transaction.Item SELECT 4. nhưng nó hơi khác ở phương thức hoạt động. hai lệnh select ở cửa sổ đầu sẽ cho kết quả khác nhau. đòi hỏi về tính toàn vẹn dữ liệu càng cao và càng có nhiều tình huống một transaction ngăn không cho các transaction khác truy . 4.Item COMMIT và cửa sổ 2 bằng INSERT INTO dbo.Item WAITFOR DELAY '00:00:10' --wait for 10 seconds SELECT * FROM dbo. và phần bộ nhớ này là cần cho mỗi transaction do đó có thể tăng lên rất lớn. Nếu bạn thay cửa sổ 1 bằng đoạn code SET TRANSACTION ISOLATION LEVEL SERIALIZABLE BEGIN TRAN SELECT * FROM dbo. Snapshot Mức độ này cũng đảm bảo độ cô lập tương đương với Serializable. Khi transaction đang select các bản ghi. Tuy nhiên cái giá kèm theo là cần thêm bộ nhớ để lưu bản sao của các bản ghi.

nhập vào dữ liệu mà nó đang thao tác. bạn có thể đặt xuống mức read uncommited. Hiệu năng của hệ thống do đó bị giảm đi. Bảng dưới đây tóm tắt các tính năng của từng mức isolation. mức isolation read commited (mức mặc định) là phù hợp trong đa số các ứng dụng. Mức Isolation Dirty read Nonrepeatable read Phantom read Read Uncommitted Yes Yes Yes Read Committed No Yes Yes Repeatable read No No Yes Serializable No No No Snapshot No No No . Do đó nó càng tăng tình trạng locking và blocking trong database (ngoại trừ với snapshot thì tăng lượng bộ nhớ cần sử dụng). Hoặc có những chức năng cần ưu tiên tốc độ thực hiện và có thể chấp nhận một chút dữ liệu không nhất quán. Có thể một vài chức năng quan trọng (ví dụ chức năng ở trang admin update dữ liệu có ảnh hưởng đến toàn hệ thống) bạn cần tính toàn vẹn cao và phải chọn mức isolation cao hơn. Thông thường.

có một vài điểm bạn cần lưu ý khi chọn trường làm clustered index để có thể đạt hiệu quả tối ưu. Phân mảnh làm cho hệ thống phải truy xuất nhiều hơn để đọc dữ liệu. và tĩnh (một khi đã insert thì bạn không mấy quan tâm đến giá trị của nó nữa và hiếm khi cần phải . Điều này dẫn đến phân mảnh dữ liệu. Ví dụ một trường VARCHAR(100) hay trường có kiểu dữ liệu xấp xỉ như FLOAT có lẽ cần được xem xét lại. các bản ghi mới có thể được chèn vào giữa bảng. cũng vì lý do giữ cho kích thước index nhỏ. các bản ghi mới sẽ luôn được thêm vào cuối bảng. tức là các bản ghi kế tiếp nhau một cách logic nhưng lại không được lưu trữ liền kề với nhau (lưu trữ ở các trang khác nhau). Với clustered index thì tiêu chí này càng quan trọng. Cột kiểu tự tăng (IDENTITY) trong nhiều trường hợp rất phù hợp với clustered index vì nó thỏa mãn tất cả các yêu cầu trên: kích thước nhỏ (kiểu INT hoặc BIGINT). cũng làm cho clustered index bị phân mảnh. một khi đã có mặt trong bảng thì giá trị của nó cần được giữ nguyên. vì khóa của nó được dùng trong tất cả các index khác (nonclustered) của bảng để làm con trỏ tới bản ghi.Clustered Index: Chọn Trường Nào Vũ Huy Tâm Do các đặc tính của clustered index. bản thân clustered index cũng cần được cập nhật để sắp xếp bản ghi vào vị trí mới cho đúng thứ tự. Thao tác cập nhật trường clustered index do vậy rất tốn kém và nếu diễn ra thường xuyên. và đồng thời các nonclustered index khác cũng phải cập nhật theo để cho con trỏ giờ phải chứa giá trị mới. Một ứng cử viên cho clustered index cần đạt được các chỉ tiêu sau: Kích thước nhỏ: Nói chung với loại index nào thì bạn cũng nên chọn trường nhỏ để giảm kích thước của index. Trường luôn tăng: Khi giá trị mới của trường clustered index luôn tăng lên. nhất là khi cần lấy về một dải các bản ghi. Và mặc dù clustered index cho phép chứa nhiều trường (index phức hợp) nhưng bạn chỉ nên dùng một trường. Nếu giá trị này thay đổi bất kỳ. Trường tĩnh: Trường clustered index không nên bị cập nhật thường xuyên. luôn tăng. Khi nó bị cập nhật. Tốt nhất là một trường kiểu số nguyên (INT hoặc BIGINT) vì tìm kiếm theo số nguyên luôn nhanh hơn tìm kiếm theo chuỗi ký tự.

và sau đó nếu thấy không thích hợp thì chuyển sang chọn trường khác. Bạn có thể trước hết hãy dùng cột IDENTITY làm clustered index. .cập nhật).