Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check constraint doesn't work in SQL Server 2016

I have a simple example below

begin tran

CREATE TABLE [dbo].[Filters]
(
    [Id]                    INT NOT NULL IDENTITY,
    [FCode]                 varchar(30) null,
    [FVersion]              varbinary(892) null,

    CONSTRAINT [PK_Filter] PRIMARY KEY CLUSTERED ([Id]),

    CONSTRAINT [CK_Filters_FCode_FVersion]  
        CHECK (([FCode] IS NULL AND [FVersion] IS NULL) 
               OR (LEN([FCode]) > 0 AND DATALENGTH([FVersion]) > 0)), 
)

INSERT INTO [dbo].[Filters] (FCode, FVersion)
VALUES  (NULL, NULL),
        (NULL, 0x6BE348),
        ('ASD', NULL),
        ('ASD', 0x6BE348)

SELECT
    IIF(([FCode] IS NULL AND [FVersion] IS NULL) 
         OR (LEN([FCode]) > 0 AND DATALENGTH([FVersion]) > 0) , 1, 0) AS [check], *
FROM
    [dbo].[filters]

rollback

I expect that the 2nd and 3rd insert statements will cause the constraint violation. However server allows them.

In use the select statement to see that the value of the constraint check is violated for 2nd and 3rd rows. See the result

check   Id  FCode   FVersion
------------------------------
1       1   NULL    NULL
0       2   NULL    0x6BE348
0       3   ASD     NULL
1       4   ASD     0x6BE348

Any ideas?

like image 304
Andrei Fedorov Avatar asked Feb 06 '26 12:02

Andrei Fedorov


1 Answers

Check constraints only fail when the value is definitively false.

The expressions that you expect to violate the constraint evaluate to UNKNOWN.

You can see this with

SELECT CASE
         WHEN( ( [FCode] IS NULL
                 AND [FVersion] IS NULL )
                OR ( LEN([FCode]) > 0
                     AND DATALENGTH([FVersion]) > 0 ) ) THEN 'True'
         WHEN NOT ( ( [FCode] IS NULL
                      AND [FVersion] IS NULL )
                     OR ( LEN([FCode]) > 0
                          AND DATALENGTH([FVersion]) > 0 ) ) THEN 'False'
         ELSE 'Unknown'
       END                    AS [check],
       LEN([FCode])           AS LenFCode,
       DATALENGTH([FVersion]) AS DataLengthFVersion,
       *
FROM   [dbo].[filters] 

Which returns

+---------+----------+--------------------+----+-------+----------+
|  check  | LenFCode | DataLengthFVersion | Id | FCode | FVersion |
+---------+----------+--------------------+----+-------+----------+
| True    | NULL     | NULL               |  1 | NULL  | NULL     |
| Unknown | NULL     | 3                  |  2 | NULL  | 0x6BE348 |
| Unknown | 3        | NULL               |  3 | ASD   | NULL     |
| True    | 3        | 3                  |  4 | ASD   | 0x6BE348 |
+---------+----------+--------------------+----+-------+----------+

The reason they are UNKNOWN is because LEN and DATALENGTH both return NULL when passed NULL

like image 200
Martin Smith Avatar answered Feb 09 '26 08:02

Martin Smith



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!