Làm thế nào các ràng buộc máy chủ SQL của tôi bị bỏ qua?


8

Chúng tôi đã tìm thấy một số hàng trong DB của chúng tôi vi phạm ràng buộc hoạt động. Sao có thể như thế được?

Ràng buộc đang hoạt động, vì chúng ta không thể thêm một hàng thủ công bỏ qua ràng buộc này. Tuy nhiên, khi chúng tôi chạy CHECKCONSTRAINTS(Files), chúng tôi thấy rằng nó đã bị bỏ qua trong một số ít lần trong quá trình chạy thử của chúng tôi. Tất cả các hàng trong câu hỏi được tạo ra trong vòng nửa giây của nhau, cho thấy một số điều kiện chủng tộc.

Đây là ràng buộc được áp dụng cho bảng. Quy tắc này có nghĩa là để đảm bảo tính duy nhất của tên trong thư mục cha đã cho:

ALTER TABLE Files ADD CONSTRAINT UniqueNameInParentFolder CHECK
    CheckUniqueNameInFolder(ParentFoldersID, Name) = 1;

Ràng buộc này gọi một hàm trông như thế này:

-- first check for the new name in the Folders table
IF ((SELECT COUNT(*) FROM Folders 
     WHERE ParentFoldersID = @FoldersID AND Name = @Name) = 0)
BEGIN 
    -- then check for it in the Files table
    IF ((SELECT COUNT(*) FROM Files 
         WHERE ParentFoldersID = @FoldersID AND Name = @Name) <= 1)
        RETURN 1
END
RETURN 0

Các hàng riêng lẻ được thêm vào bên trong các giao dịch, vì vậy tôi rất khó hiểu làm thế nào các hàng trùng lặp đang lén lút vượt qua ràng buộc này.


Vâng, chúng tôi có một hạn chế tương tự Foldersđang nhìn thấy một số lượng vi phạm tương tự ở đó là tốt. Chúng tôi hiện đang sử dụng READ_COMMITTED_SNAPSHOT.
ladenedge

Câu trả lời:


16

Kiểm tra các ràng buộc dựa trên UDF là rác. Đồng thời, RBAR, cô lập, vv như bạn đã tìm ra. Một số liên kết:

Cách an toàn nhất cho SQL Server trong trường hợp này là sử dụng các ràng buộc tiêu chuẩn như khóa duy nhất và khóa ngoài. Tôi không thể thấy lý do tại sao bạn kiểm tra bảng thư mục cho một ràng buộc trên bảng tập tin mặc dù

Chỉnh sửa: để ngăn chặn một tệp và một thư mục có cùng tên trong một thư mục mẹ nhất định , hãy sử dụng chế độ xem được lập chỉ mục. Các tệp trùng lặp hoặc các thư mục trùng lặp đòi hỏi tính duy nhất ở mức bảng.

CREATE VIEW CheckUnique
WITH SCHEMABINDING
AS
SELECT fo.ParentFoldersID, fo.Name
FROM
   Folders fo
   JOIN
   File fi ON fo.ParentFoldersID  = fi.ParentFoldersID AND fo.Name = fi.Name
GO
CREATE UNIQUE CLUSTERED INDEX IXCU_CheckUnique ON CheckUnique (ParentFoldersID, Name)
GO

Hoặc một kích hoạt.

Nhưng không bao giờ UDF trong một ràng buộc kiểm tra


Trong trường hợp của chúng tôi, các thư mục và tệp cùng tên có thể không tồn tại trong cùng thư mục mẹ, vì vậy thật không may, tôi không nghĩ các ràng buộc duy nhất / fk là đủ.
ladenedge

1
+1 Một ví dụ thú vị khác của Tibor Karaszi: sqlblog.com/bloss/tibor_karazi/archive/2009/12/17/ trên nó cho thấy cách kiểm tra các ràng buộc với udfs có âm tính giả.
AK

12

Chức năng CheckUniqueNameInFolder hầu như không kiểm tra bất cứ điều gì. Rất nhiều bản sao có thể được thêm vào dưới sự kiểm tra ràng buộc đó. Nó có hai CHỌN riêng biệt được chạy tuần tự (do đó điều kiện được kiểm tra bởi lựa chọn đầu tiên có thể bị vô hiệu hóa vào thời điểm lần thứ hai chạy) và, trong mọi trường hợp, ràng buộc nói rằng, tốt nhất không có trùng lặp khi kiểm tra xảy ra, không có cách nào làm nó nói rằng có không trùng lặp khi chèn / cập nhật xảy ra. Vì các kiểm tra không khóa các khóa được xác thực ở chế độ U hoặc X, nên nhiều lần chèn có thể xảy ra đồng thời, thực hiện kiểm tra, không tìm thấy trùng lặp và tất cả tiến hành chèn cùng một mục.

Cách duy nhất để thực thi đúng một ràng buộc duy nhất là sử dụng một ràng buộc duy nhất .

Tạo một cột được tính toán với đường dẫn đầy đủ của 'tệp' của bạn và thực thi tính duy nhất trên toàn bộ đường dẫn với một ràng buộc ĐỘC ĐÁO hoặc có thể sử dụng một ràng buộc KHÔNG GIỚI HẠN (ParentFolderID, Name). Không lưu trữ Thư mục và Tệp riêng biệt, hãy sử dụng bảng chung cho cả Thư mục và Tệp (ví dụ: Mục nhập) vì chúng chiếm cùng một không gian tên.


1
Có, vì một số lý do, chúng tôi cho rằng giao dịch sẽ xử lý các vấn đề chúng tôi gặp phải, nhưng bạn nói đúng là không có khóa. (Hợp nhất các bảng là một thiết kế lại khá lớn đối với chúng tôi tại thời điểm này, nhưng dù sao cũng là một ý tưởng thú vị.)
ladenedge

4

Chạy này trong một giao dịch ở mức mặc định read committedsẽ không tải được.

Các lần đọc không loại trừ lẫn nhau và tuần tự hóa để hai giao dịch đồng thời có thể đọc rằng hàng không tồn tại. Bạn có thể thêm UPDLOCK,ROWLOCK,HOLDLOCKgợi ý cho SELECT.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.