2012-02-07 32 views
11

Bất cứ ai có thể giải thích lý do tại sao lần chèn thứ ba (có nhãn Dữ liệu truy vấn) trong mã dưới đây có được cho phép bởi SQL Server không?Tại sao ràng buộc kiểm tra của tôi không dừng việc chèn trống này?

Theo như tôi có thể nói, ràng buộc kiểm tra chỉ nên cho phép:

  • Code là null và System là null.
  • Code không phải là rỗng và System1.

Suy nghĩ đầu tiên của tôi là ANSI NULLS, nhưng đặt chúng on hoặc off không có sự khác biệt.

Đây là ví dụ đơn giản về vấn đề lớn hơn mà chúng tôi đã tìm thấy trong đơn đăng ký của mình (Hệ thống đã được kiểm tra dựa trên danh sách số - IN(1, 2, etc.)). Chúng tôi đã thay thế séc này bằng một khóa ngoại (thay vì IN) và một ràng buộc kiểm tra mới cho phép hoặc là cả hai null hoặc cả hai đều không phải là null; làm điều đó ngăn chặn chèn thứ ba.

IF EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[CK_TestCheck]') AND parent_object_id = OBJECT_ID(N'[dbo].[TestCheck]')) 
    ALTER TABLE [dbo].[TestCheck] DROP CONSTRAINT [CK_TestCheck] 
GO 

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestCheck]') AND type in (N'U')) 
    DROP TABLE [dbo].[TestCheck] 
GO 

SET ANSI_NULLS ON 
GO 

CREATE TABLE TestCheck(
    [Id] [int] IDENTITY(1,1) NOT NULL, 
    [Code] [varchar](50) NULL, 
    [System] [tinyint] NULL, 
    PRIMARY KEY CLUSTERED ([Id] ASC)) 
GO 

ALTER TABLE [dbo].[TestCheck] WITH CHECK ADD CONSTRAINT [CK_TestCheck] CHECK 
(
    ([Code] IS NULL AND [System] IS NULL) --Both null 
    OR 
    ([Code] IS NOT NULL AND [System] = 1) --Both not null ???? 
) 
GO 

ALTER TABLE [dbo].[TestCheck] CHECK CONSTRAINT [CK_TestCheck] 
GO 

--Good Data 
insert TestCheck (Code, [System]) Values(null, null); 
insert TestCheck (Code, [System]) Values('123', 1); 

--Query Data 
insert TestCheck (Code, [System]) Values('123', null); 

--Bad data stopped 
insert TestCheck (Code, [System]) Values(null, 1); 
insert TestCheck (Code, [System]) Values('123', 4); 

select * from TestCheck 
Where 
    case when 
    (
     ([Code] IS NULL AND [System] IS NULL)   --Both null 
     OR 
     ([Code] IS NOT NULL AND [System] in (1, 2, 3)) --Both not null ???? 
    ) 
    then 0 else 1 end 
    = 1 

Trả lời

11

Kết quả đánh giá ràng buộc hiện tại cho các giá trị 123, NULL là Chưa xác định.

  • ([Code] IS NULL AND [System] IS NULL) đánh giá để False
  • ([Code] IS NOT NULL AND [System] IN (1, 2, 3)) đánh giá để Undefined

Kết quả là Undefined

Check Constraint

ràng buộc CHECK từ chối giá trị mà đánh giá đến SAI. Bởi vì giá trị không được đánh giá là UNKNOWN, sự hiện diện của chúng trong biểu thức có thể ghi đè một ràng buộc.

Bạn nên thay đổi séc của mình [System] IN (1, 2, 3) thành ISNULL([System], 0) IN (1, 2, 3).

chế kiểm tra của bạn sau đó trở thành

ALTER TABLE [dbo].[TestCheck] WITH CHECK ADD CONSTRAINT [CK_TestCheck] CHECK 
(
    ([Code] IS NULL AND [System] IS NULL) --Both null 
    OR 
    ([Code] IS NOT NULL AND ISNULL([System], 0) IN (1, 2, 3)) --Both not null ???? 
) 
+2

Tôi sẽ không đặt (Sai) trong ngoặc đơn sau khi không xác định. Nó chắc chắn không sai. –

+0

@Damien_The_Unbeliever - Tôi biết ý bạn là gì nhưng tôi đã thêm nó vào dấu ngoặc đơn như những gì 'undefined' thực hiện cho kết quả cuối cùng. Tôi sẽ thêm bình luận này vào câu trả lời. –

+2

Nhưng những gì bạn đã thêm là không đúng sự thật. Nếu kết quả cuối cùng của ràng buộc kiểm tra là 'UNKNOWN', thì nó được xử lý giống như nếu nó được đánh giá là' TRUE' - đó là điều khiến OP ngạc nhiên. –

13

Chào mừng bạn đến tuyệt vời ba luận có giá trị SQL của. Như bạn có thể hoặc có thể không biết, kết quả của bất kỳ so sánh chuẩn nào với số null không phải là TRUE hoặc FALSE, nhưng UNKNOWN.

Trong mệnh đề WHERE, toàn bộ khoản phải đánh giá là TRUE.

Trong ràng buộc CHECK, toàn bộ ràng buộc phải đánh giá là không FALSE.

Vì vậy, chúng ta có:

([Code] IS NULL AND [System] IS NULL) --Both null 
OR 
([Code] IS NOT NULL AND [System] = 1) --Both not null ???? 

nào trở thành (đối với dữ liệu truy vấn):

(FALSE AND TRUE) 
OR 
(TRUE AND UNKNOWN) 

Và bất kỳ nhà điều hành với một UNKNOWN ở một bên hoặc đánh giá lại khác như UNKNOWN, vì vậy tổng thể kết quả là UNKNOWN. Đó không phải là FALSE và do đó việc đánh giá ràng buộc kiểm tra thành công.


Nếu bạn muốn System không phải là null, nó là rõ ràng nhất đối với tôi nếu bạn thêm đó là một yêu cầu rõ ràng thêm.

([Code] IS NULL AND [System] IS NULL) --Both null 
OR 
([Code] IS NOT NULL AND [System] IS NOT NULL AND [System] = 1) --Both not null ???? 

Nó có thể có vẻ một chút lẻ cách mà điều này được xác định, nhưng nó phù hợp với cách mà các ràng buộc khác làm việc - ví dụ một ràng buộc khoá ngoài có thể có các cột rỗng, và nếu bất kỳ cột nào trong số đó là null, thì không có một hàng phù hợp trong bảng được tham chiếu.

+0

Cảm ơn, tôi đã ấn tượng rằng việc thiết lập ANSI_NULL là một cách để kiểm soát hành vi này? – DaveShaw

+0

@DaveShaw - Tôi nghĩ rằng bạn cần phải đặt nó 'OFF' mà có thể có tác dụng phụ khác – JNK

+0

@ JNK Tôi đã thử cả hai và tắt, và có kết quả tương tự. – DaveShaw

Các vấn đề liên quan