SQL सर्वर के लिए यह आवश्यक है कि परिभाषा से मेल न खाने वाले डेटा से 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).

यह भी रिपोर्ट करता है कि

DBCC CHECKDB द्वारा पाई गई त्रुटियों के लिए repair_allow_data_loss न्यूनतम मरम्मत स्तर है

और अगर मरम्मत विकल्प पर लिया गया तो पूरी तरह से पूरी पंक्ति को हटा दिया जाता है क्योंकि इसमें यह बताने का कोई तरीका नहीं है कि कौन सा कॉलम दूषित है।

डिबगर संलग्न करना दिखाता है कि NEWID()प्रति पंक्ति दो बार मूल्यांकन किया जा रहा है। एक बार पहले CASEअभिव्यक्ति का मूल्यांकन किया जाता है और एक बार इसके अंदर।

यहाँ छवि विवरण दर्ज करें

संभव समाधान का उपयोग करने के लिए हो सकता है

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

जो एक कारण या किसी अन्य समस्या से बचा जाता है और केवल प्रति पंक्ति में एक बार अभिव्यक्ति का मूल्यांकन करता है।


2

टिप्पणी वार्तालाप के अनुसार, सर्वसम्मति से प्रतीत होता है कि ओपी के प्रश्न का उत्तर यह है कि यह एक बग का गठन करता है (अर्थात अवैध होना चाहिए)।

ओपी व्लादिमीर बारानोव के स्टैकऑवरफ्लो पर विश्लेषण का संदर्भ देते हैं, जहां वे राज्य करते हैं:

"पहली बार Col1 के लिए, दूसरी बार जारी कॉलम के CASE स्टेटमेंट के लिए।

ऑप्टिमाइज़र इस मामले में नहीं जानता है या इस बात की परवाह नहीं करता है कि NEWID एक गैर-नियतात्मक फ़ंक्शन है और इसे दो बार कॉल करता है। "

दूसरा रास्ता रखो, यह उम्मीद की जानी चाहिए कि [NEWID () के भीतर] col1 का वही मूल्य है जो आपने गणना करते समय डाला है।

यह बग के साथ क्या हो रहा है, इसका पर्याय बन जाएगा, जहां NEWID को Col1 के लिए बनाया गया है और फिर बने हुए कॉलम के लिए फिर से बनाया गया है:

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

मेरे परीक्षण में, अन्य गैर-नियतात्मक कार्यों जैसे कि आरएएनडी और समय मान समान बग में परिणाम नहीं हुए।

मार्टिन के अनुसार, यह Microsoft ( https://connect.microsoft.com/SQLServer/Feedback/Details/2751288 ) पर उठाया गया है जहाँ इस पृष्ठ पर टिप्पणियाँ और StackOverflow विश्लेषण (नीचे) हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.