অস্তিত্বের বাইরে থাকা COUNT সহ অস্তিত্ব পরীক্ষা করুন! … না?


35

আমি প্রায়শই পড়েছি যখন কোনও সারির অস্তিত্ব পরীক্ষা করতে হয় তখন সবসময় একটি COUNT এর পরিবর্তে উপস্থিতি দিয়েই করা উচিত ।

তবুও বেশ কয়েকটি সাম্প্রতিক পরিস্থিতিতে আমি গণনাটি ব্যবহার করার সময় একটি কার্যকারিতা উন্নতি পরিমাপ করেছি।
প্যাটার্নটি এরকম হয়:

LEFT JOIN (
    SELECT
        someID
        , COUNT(*)
    FROM someTable
    GROUP BY someID
) AS Alias ON (
    Alias.someID = mainTable.ID
)

এসকিউএল সার্ভারের "ভিতরে" কী ঘটছে তা বলার পদ্ধতিগুলির সাথে আমি পরিচিত নই তাই আমি ভাবছিলাম যে যদি উপস্থিতিগুলির সাথে একটি অনিরাপদ ত্রুটি আছে যা আমি যে পরিমাপগুলি করেছি তার পুরোপুরি ধারণা পেয়েছে (আরবিআর থাকতে পারে?)!

আপনার কি সেই ঘটনার কিছু ব্যাখ্যা আছে?

সম্পাদনা করুন:

আপনি চালাতে পারেন এমন একটি পূর্ণ স্ক্রিপ্ট এখানে:

SET NOCOUNT ON
SET STATISTICS IO OFF

DECLARE @tmp1 TABLE (
    ID INT UNIQUE
)


DECLARE @tmp2 TABLE (
    ID INT
    , X INT IDENTITY
    , UNIQUE (ID, X)
)

; WITH T(n) AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    FROM master.dbo.spt_values AS S
) 
, tally(n) AS (
    SELECT
        T2.n * 100 + T1.n
    FROM T AS T1
    CROSS JOIN T AS T2
    WHERE T1.n <= 100
    AND T2.n <= 100
)
INSERT @tmp1
SELECT n
FROM tally AS T1
WHERE n < 10000


; WITH T(n) AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    FROM master.dbo.spt_values AS S
) 
, tally(n) AS (
    SELECT
        T2.n * 100 + T1.n
    FROM T AS T1
    CROSS JOIN T AS T2
    WHERE T1.n <= 100
    AND T2.n <= 100
)
INSERT @tmp2
SELECT T1.n
FROM tally AS T1
CROSS JOIN T AS T2
WHERE T1.n < 10000
AND T1.n % 3 <> 0
AND T2.n < 1 + T1.n % 15

PRINT '
COUNT Version:
'

WAITFOR DELAY '00:00:01'

SET STATISTICS IO ON
SET STATISTICS TIME ON

SELECT
    T1.ID
    , CASE WHEN n > 0 THEN 1 ELSE 0 END AS DoesExist
FROM @tmp1 AS T1
LEFT JOIN (
    SELECT
        T2.ID
        , COUNT(*) AS n
    FROM @tmp2 AS T2
    GROUP BY T2.ID
) AS T2 ON (
    T2.ID = T1.ID
)
WHERE T1.ID BETWEEN 5000 AND 7000
OPTION (RECOMPILE) -- Required since table are filled within the same scope

SET STATISTICS TIME OFF

PRINT '

EXISTS Version:'

WAITFOR DELAY '00:00:01'

SET STATISTICS TIME ON

SELECT
    T1.ID
    , CASE WHEN EXISTS (
        SELECT 1
        FROM @tmp2 AS T2
        WHERE T2.ID = T1.ID
    ) THEN 1 ELSE 0 END AS DoesExist
FROM @tmp1 AS T1
WHERE T1.ID BETWEEN 5000 AND 7000
OPTION (RECOMPILE) -- Required since table are filled within the same scope

SET STATISTICS TIME OFF 

এসকিউএল সার্ভারে 2008 আর 2 (সাত 64৪ বিট) আমি এই ফলাফলটি পেয়েছি

COUNT সংস্করণ:

সারণী '# 455F344D'। স্ক্যান কাউন্ট 1, লজিকাল রিডস 8, ফিজিকাল রিড 0, রিড-ফরোয়ার্ড রিড 0, লব লজিকাল রিড 0, লব ফিজিকাল 0, লব রিড-
ফরোয়ার্ড 0 টেবিল '# 492FC531'। স্ক্যান কাউন্ট 1, লজিকাল রিডস 30, ফিজিকাল রিড 0, রিড-ফরোডড রিড 0, লব লজিকাল রিড 0, লব ফিজিকাল 0, লব রিড-ফরোয়ার্ড 0

এসকিউএল সার্ভার এক্সিকিউশন টাইমস:
সিপিইউ সময় = 0 এমএস, অতিবাহিত সময় = 81 এমএস।

EXISTS সংস্করণ:

সারণী '# 492FC531'। স্ক্যান কাউন্ট 1, লজিকাল রিডিং 96, ফিজিকাল রিড 0, রিড-ফরোয়ার্ড রিড 0, লব লজিকাল রিড 0, লব ফিজিকাল রিড 0, লব রিড-
ফরোয়ার্ড 0 টেবিল '# 455F344D'। স্ক্যান কাউন্ট 1, লজিকাল রিড 8, ফিজিকাল রিড 0, রিড-ফরোডড রিড 0, লব লজিকাল রিড 0, লব ফিজিকাল 0, লব রিড-ফরোয়ার্ড 0

এসকিউএল সার্ভার এক্সিকিউশন টাইমস:
সিপিইউ সময় = 0 এমএস, অতিবাহিত সময় = 76 এমএস।

উত্তর:


43

আমি প্রায়শই পড়েছি যখন কোনও সারির অস্তিত্ব পরীক্ষা করতে হয় তখন সবসময় একটি COUNT এর পরিবর্তে উপস্থিতি দিয়েই করা উচিত ।

যে কোনও কিছুর সর্বদা সত্য হওয়া খুব বিরল , বিশেষত এটি যখন ডাটাবেসে আসে। এসকিউএলে একই শব্দার্থক প্রকাশ করার জন্য বিভিন্ন ধরণের উপায় রয়েছে। যদি থাম্বের কোনও কার্যকর নিয়ম থাকে, তবে এটি উপলব্ধ সবচেয়ে প্রাকৃতিক বাক্য গঠন (এবং হ্যাঁ, এটি বিষয়গত) ব্যবহার করে কোয়েরি লিখতে পারে এবং আপনি যে ক্যোয়ারী পরিকল্পনা বা সম্পাদনটি গ্রহণ করেন তা অগ্রহণযোগ্য হলে কেবল পুনরায় লেখার বিষয়টি বিবেচনা করুন।

এটির পক্ষে মূল্যবান বিষয়গুলির জন্য, আমার নিজের বিবেচনাটি হ'ল অস্তিত্বের প্রশ্নগুলি সবচেয়ে স্বাভাবিকভাবে ব্যবহার করে প্রকাশ করা হয় EXISTS। এটি আমার অভিজ্ঞতাও ছিল যা প্রত্যাখাত বিকল্পের চেয়ে EXISTS আরও ভাল অনুকূলিতকরণ করতে ঝোঁক । ব্যবহার এবং ফিল্টারিং অন্য বিকল্প, এটি এসকিউএল সার্ভারের ক্যোয়ারী অপ্টিমাইজারটিতে কিছুটা সমর্থন পেতে দেখা যায়, তবে আমি ব্যক্তিগতভাবে এটি আরও জটিল প্রশ্নের ক্ষেত্রে বিশ্বাসযোগ্য না বলে খুঁজে পেয়েছি। যাই হোক, শুধু অনেক বেশি (রা) আমাকে ঐ বিকল্প যেকোন তুলনায় প্রাকৃতিক বলে মনে হয়।OUTER JOINNULLCOUNT(*)=0EXISTS

আমি ভাবছিলাম যে যদি উপস্থিতিগুলির সাথে একটি নিরবচ্ছিন্ন ত্রুটি আছে যা আমি যে পরিমাপগুলি করেছি তার পুরোপুরি ধারণা পেয়েছে

আপনার নির্দিষ্ট উদাহরণটি আকর্ষণীয়, কারণ এটি CASEঅভিব্যক্তিতে সাবকিউরিয়াদের (এবং EXISTSবিশেষত পরীক্ষাগুলি) সাথে অনুকূলিতকরণের উপায়টি হাইলাইট করে ।

CASE এর এক্সপ্রেশনগুলিতে সাবক্রিওরিগুলি

নিম্নলিখিত (পুরোপুরি আইনী) ক্যোয়ারী বিবেচনা করুন:

DECLARE @Base AS TABLE (a integer NULL);
DECLARE @When AS TABLE (b integer NULL);
DECLARE @Then AS TABLE (c integer NULL);
DECLARE @Else AS TABLE (d integer NULL);

SELECT
    CASE
        WHEN (SELECT W.b FROM @When AS W) = 1
            THEN (SELECT T.c FROM @Then AS T)
        ELSE (SELECT E.d FROM @Else AS E)
    END
FROM @Base AS B;

এর শব্দার্থবিদ্যাCASE যে WHEN/ELSEক্লজ হয় সাধারণত পাঠগত অনুক্রমে মূল্যায়ন করেন। উপরের ক্যোয়ারিতে, এসকিউএল সার্ভারের ELSEযদি WHENঅনুচ্ছেদটি সন্তুষ্ট হয় তবে সাবকিউরিটি একাধিক সারিতে ফেরত দিলে ত্রুটি ফিরে পাওয়া ভুল হবে। এই শব্দার্থবিজ্ঞানের প্রতি শ্রদ্ধা জানাতে, অপ্টিমাইজার এমন একটি পরিকল্পনা তৈরি করে যা পাস-থ্রু পূর্বাভাস ব্যবহার করে:

পাস-মাধ্যমে পূর্বাভাস

নেস্টেড লুপের অভ্যন্তরীণ দিকটি কেবল তখনই মূল্যায়ন করা হয় যখন পাস-থ্রো ফিডিকেট মিথ্যা দেয় returns সামগ্রিক প্রভাবটি হ'ল CASEএক্সপ্রেশনগুলি ক্রমে পরীক্ষা করা হয় এবং পূর্ববর্তী কোনও এক্সপ্রেশন সন্তুষ্ট না হলে কেবল সাবকুইরিগুলি মূল্যায়ন করা হয়।

একটি উপস্থিতি subquery সঙ্গে CASE এক্সপ্রেশন

যেখানে একটি CASEসাবকিউরি ব্যবহার করে EXISTS, লজিকাল অস্তিত্ব পরীক্ষাটি একটি আধা-যোগ হিসাবে প্রয়োগ করা হয়, তবে পরের ধারাটির প্রয়োজন হলে সেমিগুলি সাধারণত আধা-যুক্ত দ্বারা প্রত্যাখ্যান করা হবে r এই বিশেষ ধরণের আধা-যুক্তের মধ্য দিয়ে প্রবাহিত সারিগুলি একটি অর্ধ-যোগটি মিল খুঁজে পেয়েছে কিনা তা চিহ্নিত করার জন্য একটি পতাকা অর্জন করে। এই পতাকাটি প্রোব কলাম হিসাবে পরিচিত ।

বাস্তবায়নের বিশদটি হ'ল লজিকাল সাবকোয়ারিটি একটি প্রোলে কলামের সাথে একটি 'রিলেটেড জোড়' ('প্রয়োগ') দ্বারা প্রতিস্থাপিত হয়। কাজটি কোয়েরি অপ্টিমাইজার নামে একটি সরলীকরণ নিয়ম দ্বারা সম্পাদিত হয় RemoveSubqInPrj(প্রজেক্টে সাবকোয়ারিটি সরিয়ে দিন)। আমরা ট্রেস পতাকা 8606 ব্যবহার করে বিশদটি দেখতে পাচ্ছি:

SELECT
    T1.ID,
    CASE
        WHEN EXISTS 
        (
            SELECT 1
            FROM #T2 AS T2
            WHERE T2.ID = T1.ID
        ) THEN 1 
    ELSE 0
    END AS DoesExist
FROM #T1 AS T1
WHERE T1.ID BETWEEN 5000 AND 7000
OPTION (QUERYTRACEON 3604, QUERYTRACEON 8606);

EXISTSপরীক্ষা দেখানো ইনপুট ট্রিটির কিছু অংশ নীচে দেখানো হয়েছে:

ScaOp_Exists 
    LogOp_Project
        LogOp_Select
            LogOp_Get TBL: #T2
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier [T2].ID
                ScaOp_Identifier [T1].ID

এটি দ্বারা RemoveSubqInPrjনেতৃত্বাধীন কাঠামোয় রূপান্তরিত হয়:

LogOp_Apply (x_jtLeftSemi probe PROBE:COL: Expr1008)

এটি পূর্বে বর্ণিত তদন্তের সাথে বাম আধা-যুক্ত হবে। এই প্রাথমিক রূপান্তরটি এখন পর্যন্ত এসকিউএল সার্ভারের ক্যোয়ারী অপ্টিমাইজারগুলিতে একমাত্র উপলভ্য, এবং এই রূপান্তরটি অক্ষম থাকলে সংকলনটি কেবল ব্যর্থ হবে।

এই ক্যোয়ারির জন্য কার্যকর কার্যকর পরিকল্পনার আকারগুলির মধ্যে একটি হ'ল লজিকাল স্ট্রাকচারের প্রত্যক্ষ বাস্তবায়ন:

এনএলজে সেমি প্রোবের সাথে যোগ দিন

চূড়ান্ত গণনা স্কেলার CASEপ্রোব কলাম মান ব্যবহার করে প্রকাশের ফলাফলটি মূল্যায়ন করে :

স্ক্যালার এক্সপ্রেশন গণনা

যখন প্ল্যান ট্রি গাছের মূল আকৃতিটি সংরক্ষণ করা হয় যখন অপ্টিমাইজ করা হয় অন্যান্য শারীরিক যোগদানের প্রকারগুলি অর্ধ জোড়ার জন্য বিবেচনা করে। কেবল মার্জ জয় একটি প্রোব কলাম সমর্থন করে, সুতরাং একটি হ্যাশ আধা যোগদান, যদিও যুক্তিযুক্তভাবে সম্ভব, বিবেচনা করা হয় না:

প্রোব কলামের সাথে মার্জ করুন

মার্জটি লেবেলযুক্ত একটি অভিব্যক্তি আউটপুটগুলি লক্ষ্য করুন Expr1008(যে নামটি পূর্বের মতো একটি কাকতালীয়) তবে পরিকল্পনার কোনও অপারেটরের জন্য এর জন্য কোনও সংজ্ঞা প্রদর্শিত হয়নি। এটি আবার প্রোবের কলাম। আগের মতো, চূড়ান্ত গণনা স্কেলারটি মূল্যায়ন করতে এই প্রোব মানটি ব্যবহার করে CASE

সমস্যাটি হ'ল অপটিমাইজার সম্পূর্ণরূপে এমন বিকল্পগুলি অন্বেষণ করে না যা কেবল মার্জ (বা হ্যাশ) আধা যোগদানের সাথে সার্থক হয়। নেস্টেড লুপস পরিকল্পনায়, T2প্রতিটি পুনরাবৃত্তির সাথে সারি সারি মিলছে কিনা তা খতিয়ে দেখার কোনও সুবিধা নেই । মার্জ বা হ্যাশ পরিকল্পনার সাথে, এটি একটি দরকারী অপ্টিমাইজেশন হতে পারে।

যদি আমরা ক্যোয়ারিতে কোনও মিলের BETWEENশিকারী যুক্ত করি, তবে T2যা ঘটে তা হ'ল এই চেকটি প্রতিটি সারিটির জন্য মার্জ সেমি জয়েন্টের অবশিষ্টাংশ হিসাবে সম্পাদন করা হয় (কার্যকর করার পরিকল্পনায় স্পষ্ট হওয়া শক্ত, তবে এটি রয়েছে):

SELECT
    T1.ID,
    CASE
        WHEN EXISTS 
        (
            SELECT 1
            FROM #T2 AS T2
            WHERE T2.ID = T1.ID
            AND T2.ID BETWEEN 5000 AND 7000 -- New
        ) THEN 1 
    ELSE 0
    END AS DoesExist
FROM #T1 AS T1
WHERE T1.ID BETWEEN 5000 AND 7000;

অবশিষ্ট অবশেষ

আমরা আশা করবো যে BETWEENশিকারী পরিবর্তনের পরিবর্তে একটি সন্ধানের T2ফলস্বরূপ নিচে নামানো হবে । সাধারণত, অপ্টিমাইজার এটি করার বিষয়ে বিবেচনা করবে (এমনকি কোয়েরিতে অতিরিক্ত শিকারী নাও)। এটা তোলে স্বীকার উহ্য predicates ( BETWEENউপর T1এবং তাদের মধ্যে সম্পৃক্ত যোগদানের T1এবং T2একসঙ্গে পরোক্ষভাবে BETWEENউপর T2) ছাড়াই মূল প্রশ্নের সাথে টেক্সট উপস্থিত হচ্ছে। দুর্ভাগ্যক্রমে, প্রয়োগ-প্রোব প্যাটার্নটির অর্থ এটি অন্বেষণ করা হয়নি।

উভয় ইনপুটগুলিতে একত্রিত হয়ে যাওয়া সেমিতে যোগদানের জন্য সিকিউরিটি লেখার জন্য কোয়েরি লেখার উপায় রয়েছে। একটি উপায়ে ক্যুরিয়ারটি বেশ অপ্রাকৃতভাবে লিখতে জড়িত (আমি সাধারণত যে কারণে পছন্দ করি তা পরাস্ত করে EXISTS):

WITH T2 AS
(
    SELECT TOP (9223372036854775807) * 
    FROM #T2 AS T2 
    WHERE ID BETWEEN 5000 AND 7000
)
SELECT 
    T1.ID, 
    DoesExist = 
        CASE 
            WHEN EXISTS 
            (
                SELECT * FROM T2 
                WHERE T2.ID = T1.ID
            ) THEN 1 ELSE 0 END
FROM #T1 AS T1
WHERE T1.ID BETWEEN 5000 AND 7000;

শীর্ষ কৌশল কৌশল

উত্পাদনের পরিবেশে সেই ক্যোয়ারীটি লিখতে আমি খুশি হব না, এটি কেবল প্রদর্শন করা যে কাঙ্ক্ষিত পরিকল্পনার আকার সম্ভব। সত্যিকারের ক্যোয়ারীটি যদি আপনাকে CASEএই নির্দিষ্টভাবে ব্যবহার করতে হয় এবং পারফরম্যান্সের ফলে কোনও একত্রিত হওয়া আধা-যোগদানের তদন্তের পক্ষের অনুসন্ধান না করা হয়, তবে আপনি সঠিক বাক্য তৈরি করে এমন বিভিন্ন বাক্য গঠন ব্যবহার করে কোয়েরিটি লিখতে বিবেচনা করতে পারেন আরও কার্যকর কার্যকর পরিকল্পনা।


6

"COUNT টি (*) বনাম বিদ্যমান" যুক্তি চেক রেকর্ড বিদ্যমান কিনা না হয়। উদাহরণ স্বরূপ:

WHERE (SELECT COUNT(*) FROM Table WHERE ID=@ID)>0

বনাম

WHERE EXISTS(SELECT ID FROM Table WHERE ID=@ID)

আপনার এসকিউএল স্ক্রিপ্টটি COUNT(*)একটি রেকর্ড বিদ্যমান চেক হিসাবে ব্যবহার করছে না , এবং তাই আমি এটি বলব না যে এটি আপনার দৃশ্যে প্রযোজ্য।


আমি পোস্ট করা স্ক্রিপ্টের উপর ভিত্তি করে কোনও / উপসংহার?
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.