SQL Server가 정의와 일치하지 않는 데이터로 PERSISTED 열을 채우는 것이 합법입니까?


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

의견 대화에 따르면, 합의는 OP의 질문에 대한 답변이 이것이 버그를 구성한다는 것 (즉, 불법이어야 함) 인 것으로 보인다.

OP는 StackOverflow에 대한 Vladimir Baranov의 분석을 참조합니다.

"Col1의 경우 처음, 지속 된 열의 CASE 문의 경우 두 번째.

옵티마이 저는이 경우 NEWID가 결정적이지 않은 함수라는 것을 알지 못하거나 신경 쓰지 않습니다.

다시 말하면, col1 내의 [NEWID ()]는 계산할 때 삽입 한 것과 같은 값을 가질 것으로 예상됩니다.

이것은 버그에서 발생하는 것과 동의어입니다. 여기서는 NEW1이 Col1에 대해 생성 된 다음 지속 열에 대해 다시 생성됩니다.

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

필자의 테스트에서 RAND 및 시간 값과 같은 다른 비 결정적 함수는 동일한 버그를 유발하지 않았습니다.

Martin에 따르면 이 페이지에 대한 의견과 StackOverflow 분석 (아래)이있는 Microsoft ( https://connect.microsoft.com/SQLServer/Feedback/Details/2751288 ) 로 제기되었습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.