विभिन्न तालिकाओं से ORDER BY के साथ शीर्ष 1 का चयन करते समय अनुक्रमित दृश्य कैसे सेट करें


11

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

    -- +++ THE QUERY THAT I WANT TO IMPROVE PERFORMANCE-WISE +++

    SELECT TOP 1 *
    FROM    dbo.TB_test1 t1
            INNER JOIN dbo.TB_test2 t2 ON t1.PK_ID1 = t2.FK_ID1
    ORDER BY t1.somethingelse1
           ,t2.somethingelse2;


    GO

तालिका सेटअप निम्नानुसार है:

  • दो मेज़
  • वे ऊपर की क्वेरी द्वारा एक आंतरिक जुड़ने से जुड़े हुए हैं
  • और पहले से एक स्तंभ द्वारा आदेश दिया गया और फिर ऊपर क्वेरी द्वारा दूसरी तालिका से एक स्तंभ; केवल TOP 1 का चयन किया गया है
  • (नीचे दी गई स्क्रिप्ट में परीक्षण डेटा उत्पन्न करने के लिए कुछ लाइनें भी हैं, बस अगर यह समस्या को पुन: उत्पन्न करने में मदद करता है)

    -- +++ TABLE SETUP +++
    
    CREATE TABLE [dbo].[TB_test1]
        (
         [PK_ID1] [INT] IDENTITY(1, 1)  NOT NULL
        ,[something1] VARCHAR(40) NOT NULL
        ,[somethingelse1] BIGINT NOT NULL
            CONSTRAINT [PK_TB_test1] PRIMARY KEY CLUSTERED ( [PK_ID1] ASC )
        );
    
    GO
    
    create TABLE [dbo].[TB_test2]
        (
         [PK_ID2] [INT] IDENTITY(1, 1)  NOT NULL
        ,[FK_ID1] [INT] NOT NULL
        ,[something2] VARCHAR(40) NOT NULL
        ,[somethingelse2] BIGINT NOT NULL
            CONSTRAINT [PK_TB_test2] PRIMARY KEY CLUSTERED ( [PK_ID2] ASC )
        );
    
    GO
    
    ALTER TABLE [dbo].[TB_test2]  WITH CHECK ADD  CONSTRAINT [FK_TB_Test1] FOREIGN KEY([FK_ID1])
    REFERENCES [dbo].[TB_test1] ([PK_ID1])
    GO
    
    ALTER TABLE [dbo].[TB_test2] CHECK CONSTRAINT [FK_TB_Test1]
    
    GO
    
    
    -- +++ TABLE DATA GENERATION +++
    
    -- this might not be the quickest way, but it's only to set up test data
    
    INSERT INTO dbo.TB_test1
            ( something1, somethingelse1 )
    VALUES  ( CONVERT(VARCHAR(40), NEWID())  -- something1 - varchar(40)
              ,ISNULL(ABS(CHECKSUM(NewId())) % 92233720368547758078, 1)   -- somethingelse1 - bigint
              )
    
    GO 100000
    
    RAISERROR( 'Finished setting up dbo.TB_test1', 0, 1) WITH NOWAIT    
    
    GO    
    
    INSERT INTO dbo.TB_test2
            ( FK_ID1, something2, somethingelse2 )
    VALUES  ( ISNULL(ABS(CHECKSUM(NewId())) % ((SELECT MAX(PK_ID1) FROM dbo.TB_test1) - 1), 0) + 1 -- FK_ID1 - int
              ,CONVERT(VARCHAR(40), NEWID())  -- something2 - varchar(40)
              ,ISNULL(ABS(CHECKSUM(NewId())) % 92233720368547758078, 1)   -- somethingelse2 - bigint
              )
    
    GO 100000
    
    RAISERROR( 'Finished setting up dbo.TB_test2', 0, 1) WITH NOWAIT          
    
    GO
    

अनुक्रमित दृश्य को संभवतः निम्नानुसार परिभाषित किया जाना चाहिए और परिणामी TOP 1 क्वेरी नीचे है। लेकिन मुझे किन अनुक्रमों की आवश्यकता है ताकि यह क्वेरी अनुक्रमित दृश्य के बिना बेहतर प्रदर्शन करे?

    CREATE VIEW VI_test
    WITH SCHEMABINDING
    AS
        SELECT  t1.PK_ID1
               ,t1.something1
               ,t1.somethingelse1
               ,t2.PK_ID2
               ,t2.FK_ID1
               ,t2.something2
               ,t2.somethingelse2
        FROM    dbo.TB_test1 t1
                INNER JOIN dbo.TB_test2 t2 ON t1.PK_ID1 = t2.FK_ID1


    GO


    SELECT TOP 1 * FROM dbo.VI_test ORDER BY somethingelse1,somethingelse2


    GO

जवाबों:


12

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

जब तक आप SQL सर्वर एंटरप्राइज संस्करण (या समकक्ष, परीक्षण और डेवलपर) का उपयोग नहीं कर रहे हैं, तब तक आपको इसका उपयोग WITH (NOEXPAND)करने के लिए दृश्य संदर्भ पर उपयोग करना होगा। वास्तव में, भले ही आप एंटरप्राइज का उपयोग कर रहे हों, लेकिन उस संकेत का उपयोग करने के अच्छे कारण हैं ।

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

ब्याज की एक और बात यह है कि एक NOEXPANDसंकेत के बिना , अनुकूलन शुरू होने से पहले दृश्य संदर्भ हमेशा आधार क्वेरी तक विस्तारित होते हैं। जैसे-जैसे ऑप्टिमाइज़ेशन आगे बढ़ता है, ऑप्टिमाइज़र पिछली ऑप्टिमाइज़ेशन गतिविधि के आधार पर, भौतिकीकृत दृश्य पर वापस विस्तारित परिभाषा से मेल खाने में सक्षम हो भी सकता है या नहीं भी। यह लगभग निश्चित रूप से आपके साधारण प्रश्न के साथ नहीं है, लेकिन मैं इसे पूर्णता के लिए उल्लेख करता हूं।

इसलिए, NOEXPANDटेबल संकेत का उपयोग करना आपका मुख्य विकल्प है, लेकिन आप बेस टेबल कीज़ और दृश्य में ऑर्डर करने के लिए आवश्यक कॉलम को भौतिक बनाने के बारे में भी सोच सकते हैं। संयुक्त कुंजी कॉलम पर एक अद्वितीय क्लस्टर इंडेक्स बनाएं, फिर ऑर्डरिंग कॉलम पर एक अलग गैर-क्रस्टेड इंडेक्स।

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

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

मूल समाधान

CREATE VIEW VI_test
WITH SCHEMABINDING
AS
    SELECT
        t1.PK_ID1,
        t1.something1,
        t1.somethingelse1,
        t2.PK_ID2,
        t2.FK_ID1,
        t2.something2,
        t2.somethingelse2
    FROM dbo.TB_test1 t1
    INNER JOIN dbo.TB_test2 t2 
        ON t1.PK_ID1 = t2.FK_ID1;
GO
-- Brute force unique clustered index
CREATE UNIQUE CLUSTERED INDEX cuq 
ON dbo.VI_test 
    (somethingelse1, somethingelse2, PK_ID1, PK_ID2);
GO
SELECT TOP (1) * 
FROM dbo.VI_test WITH (NOEXPAND)
ORDER BY somethingelse1,somethingelse2;

निष्पादन योजना:

जानवर बल सूचकांक

एक गैर-अनुक्रमित सूचकांक का उपयोग करना

-- Minimal unique clustered index
CREATE UNIQUE CLUSTERED INDEX cuq 
ON dbo.VI_test 
    (PK_ID1, PK_ID2)
WITH (DROP_EXISTING = ON);
GO
-- Nonclustered index for ordering
CREATE NONCLUSTERED INDEX ix 
ON dbo.VI_test (somethingelse1, somethingelse2);

निष्पादन योजना:

आदेश देने के लिए गैर-अनुक्रमित सूचकांक

इस योजना में एक लुकअप है, लेकिन इसका उपयोग केवल एक पंक्ति को लाने के लिए किया जाता है।

न्यूनतम अनुक्रमित दृश्य

ALTER VIEW VI_test
WITH SCHEMABINDING
AS
    SELECT
        t1.PK_ID1,
        t2.PK_ID2,
        t1.somethingelse1,
        t2.somethingelse2
    FROM dbo.TB_test1 t1
    INNER JOIN dbo.TB_test2 t2 
        ON t1.PK_ID1 = t2.FK_ID1;
GO
-- Unique clustered index
CREATE UNIQUE CLUSTERED INDEX cuq 
ON dbo.VI_test 
    (somethingelse1, somethingelse2, PK_ID1, PK_ID2);

प्रश्न:

SELECT TOP (1)
    V.PK_ID1,
    TT1.something1,
    V.somethingelse1,
    V.PK_ID2,
    TT2.FK_ID1,
    TT2.something2,
    V.somethingelse2
FROM dbo.VI_test AS V WITH (NOEXPAND)
JOIN dbo.TB_test1 AS TT1 ON TT1.PK_ID1 = V.PK_ID1
JOIN dbo.TB_test2 AS TT2 ON TT2.PK_ID2 = V.PK_ID2
ORDER BY somethingelse1,somethingelse2;

निष्पादन योजना:

अंतिम क्वेरी योजना

यह तालिका कुंजियों को पुनर्प्राप्त किया जा रहा है (क्रम में व्यू क्लस्टरित सूचकांक से एक एकल पंक्ति प्राप्त) और उसके बाद शेष स्तंभ लाने के लिए आधार तालिकाओं पर दो एकल-पंक्ति लुकअप।

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