लक्ष्य तालिका का सबसे बड़ा भाग


71

मैं MERGEएक तालिका से पंक्तियों को सम्मिलित करने या हटाने के लिए एक कथन का उपयोग करने की कोशिश कर रहा हूं , लेकिन मैं केवल उन पंक्तियों के सबसेट पर कार्य करना चाहता हूं। इस दस्तावेज़ के लिए MERGEबहुत जोरदार चेतावनी दी गई है:

मिलान उद्देश्यों के लिए उपयोग की जाने वाली लक्ष्य तालिका से केवल कॉलम निर्दिष्ट करना महत्वपूर्ण है। यही है, स्रोत तालिका के संबंधित कॉलम की तुलना में लक्ष्य तालिका से कॉलम निर्दिष्ट करें। ऑन-क्लॉज़ में लक्ष्य तालिका में पंक्तियों को फ़िल्टर करके क्वेरी प्रदर्शन को बेहतर बनाने का प्रयास न करें, जैसे कि निर्दिष्ट करके और target_table.column_x = मान निर्दिष्ट करके नहीं। ऐसा करने से अप्रत्याशित और गलत परिणाम मिल सकते हैं।

लेकिन यह वही है जो यह प्रतीत होता है कि मुझे अपना MERGEकाम करने के लिए करना होगा।

मेरे पास जो डेटा है वह श्रेणियों के लिए कई मानक-से-जुड़ने वाली वस्तुओं की तालिका है (उदाहरण के लिए कौन-सी वस्तुएँ किन श्रेणियों में शामिल हैं) जैसे:

CategoryId   ItemId
==========   ======
1            1
1            2
1            3
2            1
2            3
3            5
3            6
4            5

आइटम की एक नई सूची के साथ एक विशिष्ट श्रेणी में सभी पंक्तियों को प्रभावी ढंग से बदलने के लिए मुझे जो करने की आवश्यकता है। ऐसा करने का मेरा प्रारंभिक प्रयास इस तरह दिखता है:

MERGE INTO CategoryItem AS TARGET
USING (
  SELECT ItemId FROM SomeExternalDataSource WHERE CategoryId = 2
) AS SOURCE
ON SOURCE.ItemId = TARGET.ItemId AND TARGET.CategoryId = 2
WHEN NOT MATCHED BY TARGET THEN
    INSERT ( CategoryId, ItemId )
    VALUES ( 2, ItemId )
WHEN NOT MATCHED BY SOURCE AND TARGET.CategoryId = 2 THEN
    DELETE ;

यह मेरे परीक्षणों में काम करता प्रतीत होता है, लेकिन मैं वही कर रहा हूं जो MSDN स्पष्ट रूप से मुझे नहीं करने की चेतावनी देता है। यह मुझे चिंतित करता है कि मैं बाद में अप्रत्याशित समस्याओं में चला जाऊंगा, लेकिन मैं अपनी MERGEविशिष्ट क्षेत्र मूल्य ( CategoryId = 2) के साथ पंक्तियों को प्रभावित करने और अन्य श्रेणियों से पंक्तियों को अनदेखा करने का कोई अन्य तरीका नहीं देख सकता ।

क्या इसी परिणाम को प्राप्त करने के लिए "अधिक सही" तरीका है? और MSDN के बारे में "अप्रत्याशित या गलत परिणाम" क्या मुझे चेतावनी दे रहे हैं?


हां, यदि यह "अप्रत्याशित और गलत परिणाम" का ठोस उदाहरण था, तो प्रलेखन अधिक उपयोगी होगा।
एके


@SQLKiwi लिंक के लिए धन्यवाद - IMO प्रलेखन बेहतर होगा यदि इसे मूल पृष्ठ से संदर्भित किया गया था।
एके

1
@AlexKuznetsov सहमत। दुर्भाग्य से, 2012 के लिए बीओएल पुनर्गठन ने कई अन्य चीजों के बीच तोड़ दिया। यह 2008 R2 प्रलेखन में काफी अच्छी तरह से जुड़ा हुआ था।
पॉल व्हाइट

जवाबों:


103

MERGEबयान एक जटिल वाक्य रचना और एक और भी जटिल कार्यान्वयन है, लेकिन अनिवार्य रूप से विचार दो तालिकाओं में शामिल होने, पंक्तियों को बदल करने के लिए किया जा (डाला, अद्यतन या हटाए गए) की जरूरत के लिए नीचे की ओर फ़िल्टर है, और फिर अनुरोध किए गए परिवर्तन करने के लिए। निम्नलिखित नमूना आंकड़ों को देखते हुए:

DECLARE @CategoryItem AS TABLE
(
    CategoryId  integer NOT NULL,
    ItemId      integer NOT NULL,

    PRIMARY KEY (CategoryId, ItemId),
    UNIQUE (ItemId, CategoryId)
);

DECLARE @DataSource AS TABLE
(
    CategoryId  integer NOT NULL,
    ItemId      integer NOT NULL

    PRIMARY KEY (CategoryId, ItemId)
);

INSERT @CategoryItem
    (CategoryId, ItemId)
VALUES
    (1, 1),
    (1, 2),
    (1, 3),
    (2, 1),
    (2, 3),
    (3, 5),
    (3, 6),
    (4, 5);

INSERT @DataSource
    (CategoryId, ItemId)
VALUES
    (2, 2);

लक्ष्य

╔════════════╦════════╗
 CategoryId  ItemId 
╠════════════╬════════╣
          1       1 
          2       1 
          1       2 
          1       3 
          2       3 
          3       5 
          4       5 
          3       6 
╚════════════╩════════╝

स्रोत

╔════════════╦════════╗
 CategoryId  ItemId 
╠════════════╬════════╣
          2       2 
╚════════════╩════════╝

वांछित परिणाम स्रोत से डेटा के साथ लक्ष्य में डेटा को बदलने के लिए है, लेकिन केवल के लिए CategoryId = 2MERGEऊपर दिए गए विवरण के बाद , हमें एक प्रश्न लिखना चाहिए जो स्रोत से जुड़ता है और केवल कुंजियों पर लक्षित होता है, और पंक्तियों में केवल पंक्तियों को फ़िल्टर करता है WHEN:

MERGE INTO @CategoryItem AS TARGET
USING @DataSource AS SOURCE ON 
    SOURCE.ItemId = TARGET.ItemId 
    AND SOURCE.CategoryId = TARGET.CategoryId
WHEN NOT MATCHED BY SOURCE 
    AND TARGET.CategoryId = 2 
    THEN DELETE
WHEN NOT MATCHED BY TARGET 
    AND SOURCE.CategoryId = 2 
    THEN INSERT (CategoryId, ItemId)
        VALUES (CategoryId, ItemId)
OUTPUT 
    $ACTION, 
    ISNULL(INSERTED.CategoryId, DELETED.CategoryId) AS CategoryId,
    ISNULL(INSERTED.ItemId, DELETED.ItemId) AS ItemId
;

यह निम्नलिखित परिणाम देता है:

╔═════════╦════════════╦════════╗
 $ACTION  CategoryId  ItemId 
╠═════════╬════════════╬════════╣
 DELETE            2       1 
 INSERT            2       2 
 DELETE            2       3 
╚═════════╩════════════╩════════╝
╔════════════╦════════╗
 CategoryId  ItemId 
╠════════════╬════════╣
          1       1 
          1       2 
          1       3 
          2       2 
          3       5 
          3       6 
          4       5 
╚════════════╩════════╝

निष्पादन योजना है: मर्ज की योजना

ध्यान दें कि दोनों टेबल पूरी तरह से स्कैन की गई हैं। हम इसे अक्षम समझ सकते हैं, क्योंकि CategoryId = 2लक्ष्य तालिका में केवल वे पंक्तियाँ ही प्रभावित होंगी। यह वह जगह है जहाँ पुस्तकें ऑनलाइन में चेतावनी आती हैं। लक्ष्य में केवल आवश्यक पंक्तियों को छूने के लिए अनुकूलन करने का एक गुमराह करने वाला प्रयास है:

MERGE INTO @CategoryItem AS TARGET
USING 
(
    SELECT CategoryId, ItemId
    FROM @DataSource AS ds 
    WHERE CategoryId = 2
) AS SOURCE ON
    SOURCE.ItemId = TARGET.ItemId
    AND TARGET.CategoryId = 2
WHEN NOT MATCHED BY TARGET THEN
    INSERT (CategoryId, ItemId)
    VALUES (CategoryId, ItemId)
WHEN NOT MATCHED BY SOURCE THEN
    DELETE
OUTPUT 
    $ACTION, 
    ISNULL(INSERTED.CategoryId, DELETED.CategoryId) AS CategoryId,
    ISNULL(INSERTED.ItemId, DELETED.ItemId) AS ItemId
;

ONक्लॉज में तर्क जुड़ने के हिस्से के रूप में लागू किया जाता है। इस मामले में, ज्वाइन एक पूर्ण बाहरी जुड़ाव है ( इस पुस्तक को ऑनलाइन प्रविष्टि क्यों देखें)। एक बाहरी भाग के रूप में लक्ष्य पंक्तियों पर श्रेणी 2 के लिए चेक को लागू करना अंततः पंक्तियों में परिणाम के साथ एक अलग मूल्य हटा दिया जाता है (क्योंकि वे स्रोत से मेल नहीं खाते):

╔═════════╦════════════╦════════╗
 $ACTION  CategoryId  ItemId 
╠═════════╬════════════╬════════╣
 DELETE            1       1 
 DELETE            1       2 
 DELETE            1       3 
 DELETE            2       1 
 INSERT            2       2 
 DELETE            2       3 
 DELETE            3       5 
 DELETE            3       6 
 DELETE            4       5 
╚═════════╩════════════╩════════╝

╔════════════╦════════╗
 CategoryId  ItemId 
╠════════════╬════════╣
          2       2 
╚════════════╩════════╝

मूल कारण एक ही कारण है ONकि जब वे WHEREक्लाज में निर्दिष्ट होते हैं तो बाहरी जुड़ाव खंड में अलग-अलग व्यवहार करते हैं । MERGEवाक्य रचना (और कार्यान्वयन में शामिल होने के खंड निर्दिष्ट के आधार पर) सिर्फ यह मुश्किल देखने के लिए कि यह ऐसा है या नहीं।

पुस्तकें ऑनलाइन में मार्गदर्शन (में विस्तार का अनुकूलन प्रदर्शन प्रविष्टि) मार्गदर्शन है कि सही अर्थ यह सुनिश्चित करना होगा का उपयोग कर व्यक्त किया जाता है प्रदान करता है MERGE, वाक्य रचना उपयोगकर्ता जरूरी सभी कार्यान्वयन विवरण, या तरीकों से अनुकूलक वैध तरीके से पुन: व्यवस्थित करने के लिए हो सकता है खाते को समझने के लिए बिना निष्पादन दक्षता कारणों के लिए चीजें।

प्रलेखन प्रारंभिक फ़िल्टरिंग को लागू करने के लिए तीन संभावित तरीके प्रदान करता है:

WHENक्लॉज में एक फ़िल्टरिंग स्थिति निर्दिष्ट करना सही परिणाम की गारंटी देता है, लेकिन इसका मतलब यह हो सकता है कि स्रोत से अधिक पंक्तियों को पढ़ा और संसाधित किया जाता है और लक्ष्य तालिकाओं की तुलना में कड़ाई से आवश्यक है (जैसा कि पहले उदाहरण में देखा गया है)।

फ़िल्टरिंग स्थिति वाले दृश्य के माध्यम से अपडेट करना भी सही परिणामों की गारंटी देता है (चूंकि दृश्य के माध्यम से अपडेट के लिए परिवर्तित पंक्तियों को एक्सेस करना आवश्यक है) लेकिन इसके लिए एक समर्पित दृश्य की आवश्यकता होती है, और एक वह जो विचारों को अपडेट करने के लिए विषम परिस्थितियों का पालन करता है।

एक सामान्य टेबल एक्सप्रेशन का उपयोग करकेON क्लॉज़ के लिए विधेय को जोड़ने के लिए समान जोखिम उठाया जाता है , लेकिन थोड़े अलग कारणों से। कई मामलों में यह सुरक्षित होगा, लेकिन इसकी पुष्टि करने के लिए निष्पादन योजना के विशेषज्ञ विश्लेषण की आवश्यकता है (और व्यापक व्यावहारिक परीक्षण)। उदाहरण के लिए:

WITH TARGET AS 
(
    SELECT * 
    FROM @CategoryItem
    WHERE CategoryId = 2
)
MERGE INTO TARGET
USING 
(
    SELECT CategoryId, ItemId
    FROM @DataSource
    WHERE CategoryId = 2
) AS SOURCE ON
    SOURCE.ItemId = TARGET.ItemId
    AND SOURCE.CategoryId = TARGET.CategoryId
WHEN NOT MATCHED BY TARGET THEN
    INSERT (CategoryId, ItemId)
    VALUES (CategoryId, ItemId)
WHEN NOT MATCHED BY SOURCE THEN
    DELETE
OUTPUT 
    $ACTION, 
    ISNULL(INSERTED.CategoryId, DELETED.CategoryId) AS CategoryId,
    ISNULL(INSERTED.ItemId, DELETED.ItemId) AS ItemId
;

अधिक सटीक योजना के साथ यह सही परिणाम (बार-बार नहीं) उत्पन्न करता है:

विलय योजना २

योजना केवल लक्ष्य तालिका से श्रेणी 2 के लिए पंक्तियों को पढ़ती है। यदि लक्ष्य तालिका बड़ी है, तो यह एक महत्वपूर्ण प्रदर्शन विचार हो सकता है, लेकिन MERGEसिंटैक्स का उपयोग करके इस गलत को प्राप्त करना बहुत आसान है ।

कभी-कभी, MERGEअलग-अलग डीएमएल संचालन को लिखना आसान होता है । यह दृष्टिकोण एकल से भी बेहतर प्रदर्शन कर सकता है MERGE, एक ऐसा तथ्य जो अक्सर लोगों को आश्चर्यचकित करता है।

DELETE ci
FROM @CategoryItem AS ci
WHERE ci.CategoryId = 2
AND NOT EXISTS 
(
    SELECT 1 
    FROM @DataSource AS ds 
    WHERE 
        ds.ItemId = ci.ItemId
        AND ds.CategoryId = ci.CategoryId
);

INSERT @CategoryItem
SELECT 
    ds.CategoryId, 
    ds.ItemId
FROM @DataSource AS ds
WHERE
    ds.CategoryId = 2;

मुझे पता है कि यह वास्तव में एक पुराना सवाल है ... लेकिन किसी भी मौका पर आप "एक सामान्य टेबल एक्सप्रेशन का उपयोग करके ऑन-क्लॉज में विधेय को जोड़ने के लिए समान जोखिम उठाते हैं, लेकिन थोड़ा अलग कारणों से।" मुझे पता है कि BOL में भी इसी तरह की अस्पष्ट चेतावनी है "यह विधि ON क्लॉज में अतिरिक्त खोज मापदंड को निर्दिष्ट करने के समान है और गलत परिणाम उत्पन्न कर सकती है। हम अनुशंसा करते हैं कि आप इस पद्धति का उपयोग करने से बचें ..."। CTE पद्धति मेरे उपयोग के मामले को हल करने के लिए प्रकट होती है, हालांकि मैं सोच रहा हूं कि क्या कोई ऐसा परिदृश्य है जिस पर मैं विचार नहीं कर रहा हूं।
हेनरी ली
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.