क्वेरी का अनुकूलन कैसे करें


9

मेरे पास इसके समान एक डेटाबेस संरचना है,

CREATE TABLE [dbo].[Dispatch](
    [DispatchId] [int] NOT NULL,
    [ContractId] [int] NOT NULL,
    [DispatchDescription] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Dispatch] PRIMARY KEY CLUSTERED 
(
    [DispatchId] ASC,
    [ContractId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

CREATE TABLE [dbo].[DispatchLink](
    [ContractLink1] [int] NOT NULL,
    [DispatchLink1] [int] NOT NULL,
    [ContractLink2] [int] NOT NULL,
    [DispatchLink2] [int] NOT NULL
) ON [PRIMARY]

GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (1, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (2, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (3, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (4, 1, N'Test')
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 1, 1, 2)
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 1, 1, 3)
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 3, 1, 2)
GO

डिस्पैचलिंक तालिका का उद्देश्य दो डिस्पैच रिकॉर्ड को एक साथ जोड़ना है। वैसे मैं विरासत के कारण अपनी प्रेषण तालिका पर एक समग्र प्राथमिक कुंजी का उपयोग कर रहा हूं, इसलिए मैं इसे बहुत दर्द के बिना नहीं बदल सकता। इसके अलावा लिंक तालिका ऐसा करने का सही तरीका नहीं हो सकता है? लेकिन फिर से विरासत।

तो मेरा सवाल है, अगर मैं इस प्रश्न को चलाता हूं

select * from Dispatch d
inner join DispatchLink dl on d.DispatchId = dl.DispatchLink1 and d.ContractId = dl.ContractLink1
or d.DispatchId = dl.DispatchLink2 and d.ContractId = dl.ContractLink2

मैं डिस्पैचलिंक टेबल पर इंडेक्स की तलाश करने के लिए इसे कभी नहीं प्राप्त कर सकता हूं। यह हमेशा एक पूर्ण सूचकांक स्कैन करता है। यह कुछ रिकॉर्ड के साथ ठीक है, लेकिन जब आपके पास उस तालिका में 50000 है तो यह क्वेरी योजना के अनुसार सूचकांक में 50000 रिकॉर्ड को स्कैन करता है। ऐसा इसलिए है क्योंकि ज्वाइन क्लॉज में 'ands' और 'ors' होते हैं, लेकिन मैं अपना सिर इधर-उधर नहीं कर सकता कि SQL इंडेक्स की एक जोड़ी क्यों नहीं कर सकता, बजाए 'या' के बाईं ओर के, और 'या' के दाईं ओर एक है।

मैं इसके लिए स्पष्टीकरण चाहता हूं, क्वेरी को तेजी से बनाने का सुझाव नहीं जब तक कि क्वेरी को समायोजित किए बिना ऐसा नहीं किया जा सकता। कारण यह है कि मैं उपरोक्त क्वेरी को मर्ज प्रतिकृति ज्वाइन फ़िल्टर के रूप में उपयोग कर रहा हूं, इसलिए मैं दुर्भाग्य से किसी अन्य प्रकार के क्वेरी में नहीं जोड़ सकता।

अद्यतन: उदाहरण के लिए, इन प्रकारों को मैं जोड़ रहा हूं,

CREATE NONCLUSTERED INDEX IDX1 ON DispatchLink (ContractLink1, DispatchLink1)
CREATE NONCLUSTERED INDEX IDX2 ON DispatchLink (ContractLink2, DispatchLink2)
CREATE NONCLUSTERED INDEX IDX3 ON DispatchLink (ContractLink1, DispatchLink1, ContractLink2, DispatchLink2)

इसलिए यह इंडेक्स का उपयोग करता है, लेकिन पूरे इंडेक्स पर एक इंडेक्स स्कैन करता है, इसलिए 50000 रिकॉर्ड यह इंडेक्स में 50000 रिकॉर्ड को स्कैन करता है।


क्या आपके पास DispatchLinkटेबल पर कोई इंडेक्स है ?
23 अक्टूबर को ypercube y

मैंने उन अनुक्रमणिकाओं को जोड़ा है जो मैंने ऊपर की कोशिश की हैं।
पीटर

अपनी क्वेरी में: "से * चुनें * डिस्पैच डी इनर ज्वाइन करें डिस्पैचलिंक डीएल को d.DispatchId = dl.DispatchLink1 और d.ContractId = dl.ContractLink1 या d.DispatchId = dl.DispatchLink2 और d.ContractId = dl.ContontId पर शामिल करें। "OR" की स्थिति और इसे 2 का चयन करें और कोई "OR" का उपयोग करके UNION द्वारा प्रतिस्थापित करें, केवल "टेस्ट" के बजाय दोनों चयनों में केवल कुंजी कॉलम का उपयोग करें, परीक्षण को यथासंभव शुद्ध बनाने के लिए।
NoChance 23

धन्यवाद SQL कीवी, यह कुछ ऐसा है जो मैंने पहले कोशिश की थी लेकिन यह दुर्भाग्य से काम नहीं किया।
पीटर

1
क्या आपके पास प्रतिकृति का एक सरल प्रश्न हो सकता है: डिस्पैच डी इनर से डिसपैचलिंक डीएल पर डी का चयन करें। ...
एके

जवाबों:


12

ऑप्टिमाइज़र कई प्लान विकल्पों पर विचार कर सकता है (जिसमें एक से अधिक साइक्स वाले लोग भी शामिल हैं), लेकिन असहमति ( ORभविष्यवाणी) के लिए यह डिफ़ॉल्ट रूप से इंडेक्स चौराहों से जुड़ी योजनाओं पर विचार नहीं करता है। अनुक्रमित को देखते हुए:

CREATE CLUSTERED INDEX cx 
ON dbo.DispatchLink (DispatchLink1, ContractLink1);

CREATE NONCLUSTERED INDEX nc1 
ON dbo.DispatchLink (DispatchLink2, ContractLink2);

हम इंडेक्स सॉक्स (SQL सर्वर 2008 या बाद में मानकर) को बाध्य कर सकते हैं:

SELECT * 
FROM dbo.Dispatch AS d
INNER JOIN dbo.DispatchLink AS dl WITH (FORCESEEK) ON 
    (d.DispatchId = dl.DispatchLink1 AND d.ContractId = dl.ContractLink1)
    OR (d.DispatchId = dl.DispatchLink2 AND d.ContractId = dl.ContractLink2);

FORCESEEK योजना

अपने सैंपल डेटा का उपयोग करते हुए, स्कैन प्लान के लिए 0.0068057 की तुलना में प्लान की लागत 0.0332551 यूनिट है।

स्कैन योजना

हमारे द्वारा कोशिश किए जा सकने वाले संभावित रीराइट और संकेत के सभी प्रकार हैं। एक विकल्प को बढ़ावा देने के लिए फिर से लिखना का एक उदाहरण ऑप्टिमाइज़र मूल योजना के लिए विचार नहीं करता है:

SELECT * 
FROM dbo.Dispatch AS d
CROSS APPLY
(
    SELECT TOP (1) * FROM
    (
        SELECT * FROM dbo.DispatchLink AS dl
        WHERE dl.DispatchLink1 = d.DispatchId
        AND dl.ContractLink1 = d.ContractId
        UNION ALL
        SELECT * FROM dbo.DispatchLink AS dl
        WHERE dl.DispatchLink2 = d.DispatchId
        AND dl.ContractLink2 = d.ContractId
    ) SQ1
) AS F1;

यह निष्पादन योजना दूसरे सूचकांक की तलाश नहीं करती है अगर यह पहले पर एक मैच पाता है:

आवेदन शीर्ष योजना

यह डिफ़ॉल्ट FORCESEEKयोजना की तुलना में बहुत बेहतर प्रदर्शन कर सकता है ।

किसी भी नए सूचकांक को जोड़ने के बिना, हम डिस्पैच टेबल में भी किसी को मजबूर कर सकते हैं:

SELECT * 
FROM dbo.DispatchLink AS dl
JOIN dbo.Dispatch AS d WITH (FORCESEEK) ON
    (d.DispatchId = dl.DispatchLink1 AND d.ContractId = dl.ContractLink1)
    OR (d.DispatchId = dl.DispatchLink2 AND d.ContractId = dl.ContractLink2);

तलाश २

यह पहले उदाहरण की तुलना में बेहतर या बदतर हो सकता है जैसे कि तालिकाओं में प्रत्येक में कितनी पंक्तियाँ हैं। APPLY + TOPसुधार अभी भी संभव है:

SELECT * 
FROM dbo.DispatchLink AS dl
CROSS APPLY
(
    SELECT TOP (1) * FROM
    (
        SELECT * FROM dbo.Dispatch AS d
        WHERE dl.DispatchLink1 = d.DispatchId
        AND dl.ContractLink1 = d.ContractId
        UNION ALL
        SELECT * FROM dbo.Dispatch AS d
        WHERE dl.DispatchLink2 = d.DispatchId
        AND dl.ContractLink2 = d.ContractId
    ) SQ1
) AS F1;

यह बहुत उपयोगी उत्तर है। मैंने एक और प्रश्न पूछा है dba.stackexchange.com/questions/23773/analysing-a-query-plan जो वास्तविक डेटा पर वास्तविक क्वेरी योजना दिखाता है (मेरा परीक्षण डेटा नहीं)। मेरे पास यह समझने के लिए ज्ञान नहीं है कि प्रश्न योजना पर अड़चन क्या है। शायद आप देख सकते हैं?
याचिकाकर्ता

यह वास्तविक दिलचस्प है क्योंकि 'FORCESEEK' जोड़ने से मेरी क्वेरी 10 मिनट से अधिक समय के बजाय 9 सेकंड में चलती है। अपडेट के आंकड़ों से कोई फर्क नहीं पड़ता। क्वेरी एनालाइज़र क्यों गलत होगा?
याचिकाकर्ता

मुझे लगता है कि आप डिजाइन के संबंध में सही हैं। कॉलम दोहराने के बारे में आपका क्या मतलब है? आप एक टेबल संरचना को कैसे डिजाइन करेंगे जो संबंधित होने के नाते दो डिस्पैच रिकॉर्ड को एक साथ जोड़ना होगा? यह स्पष्ट करने के लिए कि 'असली' टेबल के पास अपना प्राथमिक कुंजी क्षेत्र है, लेकिन हाँ डिस्पैच में एक समग्र कुंजी होने से वास्तव में मदद नहीं मिलती है।
याचिकाकर्ता

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