SQL Server'ın PERSISTED sütunları tanımla eşleşmeyen verilerle doldurması yasal mı?


16

Hesaplanan bir sütundaki garip değerler hakkında bu soruyu takip ediyorum PERSISTED. Bu sorunun cevabı, bu davranışın nasıl ortaya çıktığı hakkında birkaç tahmin yapıyor.

Şu soruyu soruyorum: Bu açık bir hata değil mi? Are PERSISTEDsütunlar Hiç bu şekilde davranmasına izin?

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

Hesaplanan sütunun değerleri tanımına karşılık gelmediğinden, verilerin "imkansız" göründüğünü unutmayın.

Sorgularda deterministik olmayan fonksiyonların garip davranabileceği iyi bilinir, ancak burada bu, hesaplanmış sütunların kalıcılığını ihlal ettiği ve bu nedenle yasadışı olması gerektiği görülmektedir.

Rastgele sayılar eklemek tartışmalı bir senaryo olabilir, ancak NEWID()değerler ekleseydik ya da SYSUTCDATETIME()? Bence bu pratik olarak kendini gösterebilecek ilgili bir konudur.

Yanıtlar:


9

Bu kesinlikle bir hata. Aslında col1değerler oldu ne için doğru değeri değişmez açıkça rasgele sayılar içeren bir ifadenin sonucu olduğu col2olması gerekiyordu. DBCC CHECKDBkalıcı bir tabloya karşı çalıştırılırsa hata döndürür.

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

(Bir "imkansız" satırı olan test çalışmam için)

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

Ayrıca şunu da bildirir:

repair_allow_data_loss, DBCC CHECKDB tarafından bulunan hatalar için minimum onarım düzeyidir

Ve onarım seçeneğine alınırsa, hangi sütunun bozuk olduğunu söylemenin hiçbir yolu olmadığından, tüm satırı belirsiz bir şekilde siler.

Bir hata ayıklayıcı eklemek, NEWID()eklenen satır başına iki kez değerlendirildiğini gösterir . Bir kez CASEifade değerlendirilir ve bir kez ifade edilir.

resim açıklamasını buraya girin

Kullanmak için olası bir çözüm olabilir

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

Hangi sebepten ötürü sorunu önler ve ifadeyi her satırda yalnızca bir kez değerlendirir.


2

Yorum konuşmasına göre fikir birliği, OP'nin sorusunun cevabının bunun bir hata oluşturduğu (yani yasadışı olması gerektiği) gibi görünüyor.

OP, Vladimir Baranov'un StackOverflow üzerindeki analizini, belirttikleri yerlere atıfta bulunuyor:

"Col1 için ilk kez, kalıcı sütunun CASE deyimi için ikinci kez.

Optimizer bilmiyor veya bu durumda NEWID'in deterministik olmayan bir işlev olduğunu ve iki kez çağırdığını umursamıyor. "

Başka bir deyişle, col1 içindeki [NEWID () öğesinin hesaplama yaparken girdiğiniz değerle aynı olması beklenir.

Bu, Col1 için NEWID ve sonra da kalıcı sütun için tekrar oluşturulan hata ile olanlarla eşanlamlı olur:

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

Testlerimde, RAND ve zaman değerleri gibi diğer deterministik olmayan fonksiyonlar aynı hataya yol açmadı.

Martin'e göre bu, Microsoft'a ( https://connect.microsoft.com/SQLServer/Feedback/Details/2751288 ) bu sayfaya ve StackOverflow analizine (aşağıda) yorum yapıldı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.