Чи законно SQL Server заповнювати ПЕРСИСТИЧНІ стовпці даними, які не відповідають визначенню?


16

Я переглядаю це питання про дивні значення в PERSISTEDобчисленій колонці. Відповідь там дає кілька здогадок про те, як склалася така поведінка.

Я запитую наступне: це не відверта помилка? Чи PERSISTEDдозволяється колись колонкам поводитись так?

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
*/

Зауважте, що дані видаються "неможливими", оскільки значення обчисленого стовпця не відповідають його визначенню.

Добре відомо, що недетерміновані функції в запитах можуть вести себе дивним чином, але тут, здається, це порушує договір збережених обчислених стовпців і, отже, повинно бути незаконним.

Вставка випадкових чисел може бути надуманим сценарієм, але що робити, якщо ми вставляли NEWID()значення чи SYSUTCDATETIME()? Я думаю, що це відповідне питання, яке може практично проявитись.

Відповіді:


9

Це, звичайно, помилка. Той факт, що col1значення трапилися в результаті вираження, що включає випадкові числа, явно не змінює того, яким має бути правильне значення col2. DBCC CHECKDBповертає помилку, якщо це запущено проти постійної таблиці.

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

Дає (для мого тестового запуску, який мав один "неможливий" рядок)

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).

Він також повідомляє про це

repair_allow_data_loss - це мінімальний рівень ремонту помилок, виявлених DBCC CHECKDB

І якщо взяти на себе опцію ремонту, безцеремонно видаляє весь ряд, оскільки він не може сказати, який стовпчик пошкоджений.

Приєднання налагоджувача показує, що значення NEWID()оцінюється двічі на вставлений рядок. Один раз перед CASEвиразом оцінюється і один раз всередині нього.

введіть тут опис зображення

Можливе вирішення може бути використане

INSERT INTO @test
            (Col1)
SELECT ( ABS(CHECKSUM(NEWID()) % 5) )
FROM   (VALUES (1),(1),(1),(1),(1)) V(X); 

Що з тих чи інших причин уникає проблеми та оцінює вираз лише один раз у ряд.


2

Згідно з розмовою з коментарями, мабуть, існує консенсус, що відповідь на питання ОП полягає в тому, що це не є помилкою (тобто має бути незаконним).

ОП посилається на аналіз Володимира Баранова щодо StackOverflow, де вони заявляють:

"Перший раз для Col1, другий раз для заяви CASE збереженої колонки.

Оптимізатор не знає, або в цьому випадку байдуже, що NEWID є недетермінованою функцією і викликає її двічі ".

По-іншому, слід очікувати, що [NEWID () в межах] col1 має те саме значення, яке ви тільки що вставили, як і під час обчислення.

Це було б синонімом того, що відбувається з помилкою, коли NEWID створюється для Col1, а потім знову створюється для збереженого стовпця:

INSERT INTO @Test (Col1, Contains2) VALUES
(NEWID(), CASE WHEN (NEWID()) LIKE '%2%' THEN 1 ELSE 0 END)

У моєму тестуванні інші недетерміновані функції, такі як значення RAND та часу, не призвели до тієї ж помилки.

Пертину, це було звернено до Microsoft ( https://connect.microsoft.com/SQLServer/Feedback/Details/2751288 ), де на цій сторінці є коментарі та аналіз StackOverflow (нижче).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.