I'm following up on [this question][1] about strange values in a `PERSISTED` computed column. The answer there makes a few guesses about how this behavior came to be.
I'm asking the following: Is this not an outright bug? Are `PERSISTED` columns ever allowed to behave this way?
DECLARE @test TABLE (
Col1 INT,
Contains2 AS CASE WHEN 2 IN (Col1) THEN 1 ELSE 0 END PERSISTED) --depends on Col1
INSERT INTO @test (Col1) VALUES
(ABS(CHECKSUM(NEWID()) % 5)),
(ABS(CHECKSUM(NEWID()) % 5)),
(ABS(CHECKSUM(NEWID()) % 5)),
(ABS(CHECKSUM(NEWID()) % 5)),
(ABS(CHECKSUM(NEWID()) % 5))
SELECT * FROM @test --shows impossible data
UPDATE @test SET Col1 = Col1*1 --"fix" the data by rewriting it
SELECT * FROM @test --observe fixed data
/*
Col1 Contains2
2 0
2 0
0 1
4 0
3 0
Col1 Contains2
2 1
2 1
0 0
4 0
3 0
*/
Note, that the data appears "impossible" because the values of the computed column do not correspond to its definition.
It is well known that non-deterministic functions in queries can behave strangely but here this seems to violate the contract of persisted computed columns and, therefore, should be illegal.
Inserting random numbers might be a contrived scenario but what if we were inserting `NEWID()` values or `SYSUTCDATETIME()`? I think this is a relevant issue that might practically manifest itself.
[1]: https://stackoverflow.com/questions/38120684/inconsistent-results-with-newid-and-persistent-computed-column
This is certainly a bug. The fact that the `col1` values happened to be the result of an expression involving random numbers clearly doesn't change what the the correct value for `col2` is supposed to be. `DBCC CHECKDB` returns an error if this is run against a permanent table.
create table test (
Col1 INT,
Contains2 AS CASE WHEN 2 IN (Col1) THEN 1 ELSE 0 END PERSISTED);
INSERT INTO test (Col1) VALUES
(ABS(CHECKSUM(NEWID()) % 5)),
(ABS(CHECKSUM(NEWID()) % 5)),
(ABS(CHECKSUM(NEWID()) % 5)),
(ABS(CHECKSUM(NEWID()) % 5)),
(ABS(CHECKSUM(NEWID()) % 5));
DBCC CHECKDB
Gives (for my test run which had one "impossible" row)
Msg 2537, Level 16, State 106, Line 17
Table error: object ID 437576597, index ID 0, partition ID 72057594041008128, alloc unit ID 72057594046251008 (type In-row data), page (1:121), row 0. The record check (valid computed column) failed. The values are 2 and 0.
DBCC results for 'test'.
There are 5 rows in 1 pages for object "test".
CHECKDB found 0 allocation errors and 1 consistency errors in table 'test' (object ID 437576597).
It does also report that
> repair_allow_data_loss is the minimum repair level for the errors
> found by DBCC CHECKDB
And if taken up on the repair option unceremoniously deletes the whole row as it has no way of telling which column is corrupted.
Attaching a debugger shows that the `NEWID()` is being evaluated twice per inserted row. Once before the `CASE` expression is evaluated and once inside it.
[![enter image description here][1]][1]
A possible workaround might be to use
INSERT INTO @test
(Col1)
SELECT ( ABS(CHECKSUM(NEWID()) % 5) )
FROM (VALUES (1),(1),(1),(1),(1)) V(X);
Which for one reason or another avoids the issue and only evaluates the expression once per row.
[1]: https://i.stack.imgur.com/HHKF7.png