दृश्यता चेतावनी :दूसरा जवाब मत दो। यह गलत मान देगा। पढ़ें कि यह गलत क्यों है।
UPDATE
बनाने के लिए आवश्यक कीचड़ को देखते हुए OUTPUT
के साथ SQL Server 2008 R2 में काम करते हैं, मैंने अपनी क्वेरी को इससे बदल दिया है:
UPDATE BatchReports
SET IsProcessed = 1
OUTPUT inserted.BatchFileXml, inserted.ResponseFileXml, deleted.ProcessedDate
WHERE BatchReports.BatchReportGUID = @someGuid
करने के लिए:
SELECT BatchFileXml, ResponseFileXml, ProcessedDate FROM BatchReports
WHERE BatchReports.BatchReportGUID = @someGuid
UPDATE BatchReports
SET IsProcessed = 1
WHERE BatchReports.BatchReportGUID = @someGuid
मूल रूप से मैंने OUTPUT
. का उपयोग करना बंद कर दिया है . यह इतना बुरा नहीं है जितना इकाई फ्रेमवर्क स्वयं इसी हैक का उपयोग करता है!
उम्मीद है <स्ट्राइक>2012स्ट्राइक> <स्ट्राइक>2014स्ट्राइक> <स्ट्राइक>2016स्ट्राइक> <स्ट्राइक>2018स्ट्राइक> <स्ट्राइक>2019स्ट्राइक> 2020 का क्रियान्वयन बेहतर होगा।
अपडेट:OUTPUT का उपयोग करना हानिकारक है
जिस समस्या से हमने शुरुआत की वह OUTPUT
. का उपयोग करने का प्रयास कर रही थी "बाद" . को पुनः प्राप्त करने के लिए खंड तालिका में मान:
UPDATE BatchReports
SET IsProcessed = 1
OUTPUT inserted.LastModifiedDate, inserted.RowVersion, inserted.BatchReportID
WHERE BatchReports.BatchReportGUID = @someGuid
यह तब सुप्रसिद्ध सीमा को हिट करता है ("यह ठीक नहीं करेगा" बग) SQL सर्वर में:
<ब्लॉकक्वॉट>DML स्टेटमेंट की टारगेट टेबल 'बैचरिपोर्ट्स' में कोई सक्षम ट्रिगर नहीं हो सकता है अगर स्टेटमेंट में INTO क्लॉज के बिना OUTPUT क्लॉज है
समाधान प्रयास #1
इसलिए हम कुछ ऐसा करने की कोशिश करते हैं जहां हम एक मध्यवर्ती TABLE
. का उपयोग करेंगे OUTPUT
को होल्ड करने के लिए वेरिएबल परिणाम:
DECLARE @t TABLE (
LastModifiedDate datetime,
RowVersion timestamp,
BatchReportID int
)
UPDATE BatchReports
SET IsProcessed = 1
OUTPUT inserted.LastModifiedDate, inserted.RowVersion, inserted.BatchReportID
INTO @t
WHERE BatchReports.BatchReportGUID = @someGuid
SELECT * FROM @t
सिवाय इसके कि विफल रहता है क्योंकि आपको timestamp
डालने की अनुमति नहीं है तालिका में (यहां तक कि एक अस्थायी तालिका चर)।
समाधान प्रयास #2
हम गुप्त रूप से जानते हैं कि एक timestamp
वास्तव में एक 64-बिट (उर्फ 8 बाइट) अहस्ताक्षरित पूर्णांक है। हम binary(8)
. का उपयोग करने के लिए अपनी अस्थायी तालिका परिभाषा को बदल सकते हैं timestamp
. के बजाय :
DECLARE @t TABLE (
LastModifiedDate datetime,
RowVersion binary(8),
BatchReportID int
)
UPDATE BatchReports
SET IsProcessed = 1
OUTPUT inserted.LastModifiedDate, inserted.RowVersion, inserted.BatchReportID
INTO @t
WHERE BatchReports.BatchReportGUID = @someGuid
SELECT * FROM @t
और यह काम करता है, सिवाय इसके कि मान गलत हैं ।
टाइमस्टैम्प RowVersion
हम वापसी टाइमस्टैम्प का मूल्य नहीं है क्योंकि यह अद्यतन पूरा होने के बाद अस्तित्व में था:
- टाइमस्टैम्प लौटाया :
0x0000000001B71692
- वास्तविक टाइमस्टैम्प :
0x0000000001B71693
ऐसा इसलिए है क्योंकि मान OUTPUT
हमारी तालिका में नहीं . हैं वे मान जैसे वे अद्यतन विवरण के अंत में थे:
- अद्यतन विवरण प्रारंभ
- पंक्ति को संशोधित करता है
- टाइमस्टैम्प अपडेट किया गया है (उदाहरण 2 → 3)
- OUTPUT नया टाइमस्टैम्प पुनर्प्राप्त करता है (यानी 3)
- ट्रिगर रन
- पंक्ति को फिर से संशोधित करता है
- टाइमस्टैम्प अपडेट किया गया है (उदाहरण 3 → 4)
- पंक्ति को फिर से संशोधित करता है
- पंक्ति को संशोधित करता है
- अद्यतन विवरण पूर्ण
- आउटपुट रिटर्न 3 (गलत मान)
इसका मतलब है:
- हमें टाइमस्टैम्प नहीं मिलता क्योंकि यह अद्यतन विवरण के अंत में मौजूद है (4 )
- इसके बजाय हमें टाइमस्टैम्प मिलता है क्योंकि यह अद्यतन विवरण के अनिश्चित मध्य में था (3 )
- हमें सही टाइमस्टैम्प नहीं मिलता
किसी . के बारे में भी यही सच है ट्रिगर जो कोई भी . को संशोधित करता है पंक्ति में मान। OUTPUT
अद्यतन के अंत तक मान को OUTPUT नहीं करेगा।
इसका मतलब है कि आप किसी भी सही मान को वापस करने के लिए OUTPUT पर भरोसा नहीं कर सकते।
इस दर्दनाक सच्चाई को BOL में दर्ज़ किया गया है:
<ब्लॉकक्वॉट>OUTPUT से लौटाए गए कॉलम डेटा को वैसा ही दर्शाते हैं जैसा कि INSERT, UPDATE, या DELETE स्टेटमेंट के पूरा होने के बाद लेकिन ट्रिगर्स के निष्पादित होने से पहले होता है।
इकाई फ्रेमवर्क ने इसे कैसे हल किया?
.NET इकाई फ्रेमवर्क आशावादी Concurrency के लिए पंक्ति संस्करण का उपयोग करता है। EF timestamp
. का मान जानने पर निर्भर करता है जैसा कि वे एक अद्यतन जारी करने के बाद मौजूद हैं।
चूंकि आप OUTPUT
. का उपयोग नहीं कर सकते हैं किसी भी महत्वपूर्ण डेटा के लिए, माइक्रोसॉफ्ट का एंटिटी फ्रेमवर्क उसी वर्कअराउंड का उपयोग करता है जो मैं करता हूं:
समाधान #3 - अंतिम - OUTPUT खंड का उपयोग न करें
बाद . को पुनः प्राप्त करने के लिए मान, इकाई फ्रेमवर्क मुद्दे:
UPDATE [dbo].[BatchReports]
SET [IsProcessed] = @0
WHERE (([BatchReportGUID] = @1) AND ([RowVersion] = @2))
SELECT [RowVersion], [LastModifiedDate]
FROM [dbo].[BatchReports]
WHERE @@ROWCOUNT > 0 AND [BatchReportGUID] = @1
OUTPUT
का उपयोग न करें ।
हाँ यह एक दौड़ की स्थिति से ग्रस्त है, लेकिन यह सबसे अच्छा SQL सर्वर कर सकता है।
INSERTs के बारे में क्या
वह करें जो एंटिटी फ्रेमवर्क करता है:
SET NOCOUNT ON;
DECLARE @generated_keys table([CustomerID] int)
INSERT Customers (FirstName, LastName)
OUTPUT inserted.[CustomerID] INTO @generated_keys
VALUES ('Steve', 'Brown')
SELECT t.[CustomerID], t.[CustomerGuid], t.[RowVersion], t.[CreatedDate]
FROM @generated_keys AS g
INNER JOIN Customers AS t
ON g.[CustomerGUID] = t.[CustomerGUID]
WHERE @@ROWCOUNT > 0
फिर से, वे एक SELECT
. का उपयोग करते हैं OUTPUT क्लॉज पर भरोसा करने के बजाय पंक्ति को पढ़ने के लिए स्टेटमेंट।