এসকিউএল সার্ভার 2014-এ 100x ধীর অনুসন্ধান, সারি গণনা স্পুল সারিতে অপরাধীর অনুমান?


13

আমার কাছে একটি কোয়েরি রয়েছে যা এসকিউএল সার্ভার ২০১২ সালে 800 মিলি সেকেন্ডে চলে এবং এসকিউএল সার্ভার 2014-এ প্রায় 170 সেকেন্ড সময় নেয় । আমি মনে করি যে আমি এটিকে Row Count Spoolঅপারেটরের খারাপ কার্ডিনালিটির প্রাক্কলন হিসাবে সংকুচিত করেছি । আমি স্পুল অপারেটরদের সম্পর্কে কিছুটা পড়েছি (যেমন, এখানে এবং এখানে ), তবে এখনও কয়েকটি জিনিস বুঝতে সমস্যা হচ্ছে:

  • এই কোয়েরিতে Row Count Spoolঅপারেটরের প্রয়োজন কেন ? আমি মনে করি না এটি সঠিকতার জন্য প্রয়োজনীয়, তাই এটি কোন নির্দিষ্ট অপ্টিমাইজেশন সরবরাহ করার চেষ্টা করছে?
  • এসকিউএল সার্ভার কেন অনুমান করে যে Row Count Spoolঅপারেটরে যোগ দেওয়া সমস্ত সারি সরিয়ে দেয়?
  • এটি কি এসকিউএল সার্ভার 2014 এ একটি বাগ? যদি তা হয় তবে আমি কানেক্টে ফাইল করব। তবে আমি প্রথমে আরও গভীর বোঝা চাই।

দ্রষ্টব্য: LEFT JOINএসকিউএল সার্ভার ২০১২ এবং এসকিউএল সার্ভার ২০১৪ উভয়ই গ্রহণযোগ্য কর্মক্ষমতা অর্জনের জন্য আমি কোয়েরিটিকে আবার লিখতে বা টেবিলগুলিতে সূচিগুলি যুক্ত করতে পারি So কীভাবে ক্যোয়ারীটি আলাদাভাবে বাক্য বলবেন।


ধীর ক্যোয়ারী

একটি সম্পূর্ণ পরীক্ষার স্ক্রিপ্টের জন্য এই পাস্তবিনটি দেখুন । এখানে আমি নির্দিষ্ট পরীক্ষার কোয়েরিটি দেখছি:

-- Prune any existing customers from the set of potential new customers
-- This query is much slower than expected in SQL Server 2014 
SELECT *
FROM #potentialNewCustomers -- 10K rows
WHERE cust_nbr NOT IN (
    SELECT cust_nbr
    FROM #existingCustomers -- 1MM rows
)


এসকিউএল সার্ভার 2014: আনুমানিক ক্যোয়ারী পরিকল্পনা

এসকিউএল সার্ভার বিশ্বাস করে যে এটি Left Anti Semi Joinথেকে Row Count Spool10,000 সারিগুলি 1 টি সারিতে ফিল্টার করবে। এই কারণে, এটি LOOP JOINপরবর্তী যোগদানের জন্য একটি নির্বাচন করে #existingCustomers

এখানে চিত্র বর্ণনা লিখুন


এসকিউএল সার্ভার 2014: আসল ক্যোয়ারী পরিকল্পনা

যেমনটি প্রত্যাশা করা হয়েছিল (এসকিউএল সার্ভার ছাড়াও সবার দ্বারা!), এর Row Count Spoolকোনও সারি সরানো হয়নি। সুতরাং যখন আমরা এসকিউএল সার্ভারটি একবার লুপ করার প্রত্যাশা করছিলাম তখন আমরা 10,000 বার লুপ করছি।

এখানে চিত্র বর্ণনা লিখুন


এসকিউএল সার্ভার ২০১২: আনুমানিক ক্যোয়ারী পরিকল্পনা

এসকিউএল সার্ভার ২০১২ ব্যবহার করার সময় (বা OPTION (QUERYTRACEON 9481)এসকিউএল সার্ভার ২০১৪), Row Count Spoolসারিগুলির আনুমানিক # কমিয়ে দেয় না এবং একটি হ্যাশ জয়েন বেছে নেওয়া হয়, এর ফলে আরও উন্নততর পরিকল্পনার ফলস্বরূপ।

এখানে চিত্র বর্ণনা লিখুন

বাম জোট আবার লিখুন

রেফারেন্সের জন্য, এখানে এমন একটি উপায় রয়েছে যে সমস্ত এসকিউএল সার্ভার ২০১২, ২০১৪ এবং ২০১ 2016 সালে ভাল পারফরম্যান্স অর্জনের জন্য আমি আবারো কোয়েরিটি আবার লিখতে পারি, তবে, আমি এখনও উপরোক্ত প্রশ্নের সুনির্দিষ্ট আচরণে আগ্রহী এবং তা কিনা নতুন এসকিউএল সার্ভার 2014 কার্ডিনালিটি এসটিমেটরের একটি বাগ।

-- Re-writing with LEFT JOIN yields much better performance in 2012/2014/2016
SELECT n.*
FROM #potentialNewCustomers n
LEFT JOIN (SELECT 1 AS test, cust_nbr FROM #existingCustomers) c
    ON c.cust_nbr = n.cust_nbr
WHERE c.test IS NULL

এখানে চিত্র বর্ণনা লিখুন

উত্তর:


10

এই কোয়েরিতে কেন একটি রো কাউন্ট স্পুল অপারেটর প্রয়োজন? ... এটি কোন নির্দিষ্ট অপ্টিমাইজেশন সরবরাহ করার চেষ্টা করছে?

cust_nbrকলামে #existingCustomersnullable হয়। যদি এটিতে কোনও নাল থাকে তবে সঠিক প্রতিক্রিয়াটি হ'ল শূন্য সারি ( NOT IN (NULL,...) সর্বদা একটি ফাঁকা ফলাফল সেট দেবে))

সুতরাং ক্যোয়ারী হিসাবে ভাবা যেতে পারে

SELECT p.*
FROM   #potentialNewCustomers p
WHERE  NOT EXISTS (SELECT *
                   FROM   #existingCustomers e1
                   WHERE  p.cust_nbr = e1.cust_nbr)
       AND NOT EXISTS (SELECT *
                       FROM   #existingCustomers e2
                       WHERE  e2.cust_nbr IS NULL) 

এর সাথে মূল্যায়ন না করে এ জন্য সারি সারি স্পাউট করুন

EXISTS (SELECT *
        FROM   #existingCustomers e2
        WHERE  e2.cust_nbr IS NULL) 

একবারের বেশী.

এটি কেবলমাত্র এমন একটি ক্ষেত্রে মনে হচ্ছে যেখানে অনুমানগুলির মধ্যে একটি সামান্য পার্থক্য পারফরম্যান্সে যথেষ্ট বিপর্যয়কর পার্থক্য আনতে পারে।

নীচে হিসাবে একটি একক সারি আপডেট করার পরে ...

UPDATE #existingCustomers
SET    cust_nbr = NULL
WHERE  cust_nbr = 1;

... কোয়েরিটি এক সেকেন্ডেরও কম সময়ে শেষ হয়েছে। পরিকল্পনার আসল এবং আনুমানিক সংস্করণগুলিতে সারি গণনা এখন প্রায় স্পট।

SET STATISTICS TIME ON;
SET STATISTICS IO ON;

SELECT *
FROM   #potentialNewCustomers
WHERE  cust_nbr NOT IN (SELECT cust_nbr
                        FROM   #existingCustomers 
                       ) 

এখানে চিত্র বর্ণনা লিখুন

জিরো সারিগুলি উপরে বর্ণিত হিসাবে আউটপুট।

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


9

এই কোয়েরিতে কেন একটি রো কাউন্ট স্পুল অপারেটর প্রয়োজন? আমি মনে করি না এটি সঠিকতার জন্য প্রয়োজনীয়, তাই এটি কোন নির্দিষ্ট অপ্টিমাইজেশন সরবরাহ করার চেষ্টা করছে?

এই প্রশ্নের জন্য মার্টিনের পূর্ণ উত্তর দেখুন । মূল বক্তব্যটি হ'ল যদি এর মধ্যে একটি একক সারি NOT INহয় NULL, তবে বুলিয়ান যুক্তি এইভাবে কাজ করে যে "সঠিক প্রতিক্রিয়াটি শূন্য সারি ফিরে পাওয়া"। Row Count Spoolঅপারেটর এই (প্রয়োজন হলে) যুক্তিবিজ্ঞান নিখুঁত করা হয়।

এসকিউএল সার্ভার কেন অনুমান করে যে সারি গণনা স্পুল অপারেটরে যোগদান করা সমস্ত সারি সরিয়ে দেয়?

মাইক্রোসফ্ট এসকিউএল 2014 কার্ডিনালিটি এসটিমেটরে একটি দুর্দান্ত সাদা কাগজ সরবরাহ করে । এই দস্তাবেজে, আমি নিম্নলিখিত তথ্যগুলি পেয়েছি:

নতুন সিই অনুমান করে যে মানটি হিস্টগ্রামের সীমার বাইরে না গিয়েও কোয়ালিটির মানগুলি ডেটাসেটে বিদ্যমান থাকে। এই উদাহরণে নতুন সিই একটি গড় ফ্রিকোয়েন্সি ব্যবহার করে যা ঘনত্ব দ্বারা সারণী কার্ডিনালিয়ালিটি গুণ করে গণনা করা হয়।

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

যাইহোক, এই নির্দিষ্ট ক্ষেত্রে, একটি NULLমান পাওয়া যাবে তা ধরে নিলে এই ধারনা বাড়ে যে দলে যোগ দেওয়া Row Count Spoolথেকে সমস্ত সারি ফিল্টার হবে #potentialNewCustomers। যে ক্ষেত্রে বাস্তবে NULLসারি রয়েছে, এটি সঠিক অনুমান (যেমন মার্টিনের উত্তরে দেখা গেছে)। যাইহোক, যেখানে NULLসারি না হওয়ার ঘটনা ঘটে সেখানে এর প্রভাবটি বিপর্যয়কর হতে পারে কারণ এসকিউএল সার্ভার কতগুলি ইনপুট সারি প্রদর্শিত হবে তা বিবেচনা না করেই 1 সারির পোস্ট-জয়েনের প্রাক্কলন উত্পাদন করে। এটি ক্যোয়ারী পরিকল্পনার বাকি অংশগুলিতে খুব দুর্বল যোগদানের পছন্দগুলিতে নিয়ে যেতে পারে।

এটি কি এসকিউএল 2014 এ একটি বাগ? যদি তা হয় তবে আমি কানেক্টে ফাইল করব। তবে আমি প্রথমে আরও গভীর বোঝা চাই।

আমি মনে করি এটি বাগ এবং পারফরম্যান্স-প্রভাবিত ধারণা এবং এসকিউএল সার্ভারের নতুন কার্ডিনালিটি অনুমানের সীমাবদ্ধতার মধ্যে ধূসর অঞ্চলে in যাইহোক, এই তাত্পর্যটি এসকিউএল ২০১২-এর তুলনায় কার্যক্ষমতার মধ্যে যথেষ্ট পরিমাণে চাপ সৃষ্টি করতে পারে NOT INযা কোনও মূল্যহীন ধারাটির নির্দিষ্ট ক্ষেত্রে সাফল্য অর্জন করে NULL

অতএব, আমি একটি সংযোগ ইস্যু দায়ের করেছি যাতে এসকিউএল দল কার্ডিনালিটি অনুমানের ক্ষেত্রে এই পরিবর্তনের সম্ভাব্য প্রভাব সম্পর্কে অবগত থাকে।

আপডেট: এসকিউএল 16 এর জন্য আমরা এখন সিটিপি 3 এ রয়েছি এবং আমি নিশ্চিত করেছি যে সমস্যাটি সেখানে ঘটে না।


5

মার্টিন স্মিথের উত্তর এবং আপনার স্ব-উত্তরটি সমস্ত মূল বিষয়গুলিকে সঠিকভাবে সম্বোধন করেছে, আমি কেবল ভবিষ্যতের পাঠকদের জন্য একটি ক্ষেত্রকে জোর দিতে চাই:

সুতরাং এই প্রশ্নটি এই নির্দিষ্ট ক্যোয়ারী এবং গভীরতার পরিকল্পনা এবং আরও কীভাবে ক্যোয়ারীটি আলাদাভাবে বাক্য বানানো যায় সে সম্পর্কে আরও পরিকল্পনা সম্পর্কে বোঝার বিষয়ে is

ক্যোয়ারির বর্ণিত উদ্দেশ্য হ'ল:

-- Prune any existing customers from the set of potential new customers

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

যৌক্তিক প্রয়োজনীয়তা সম্পূর্ণরূপে প্রকাশ করা:

  • ইতিমধ্যে গ্রাহক নয় এমন সম্ভাব্য গ্রাহকদের ফিরিয়ে দিন
  • প্রতিটি সম্ভাব্য গ্রাহককে একবারে তালিকাভুক্ত করুন
  • নাল সম্ভাব্য এবং বিদ্যমান গ্রাহকরা বাদ দিন (নাল গ্রাহক যাই হোক না কেন)

তারপরে আমরা আমাদের পছন্দের সিনট্যাক্সটি ব্যবহার করে সেই প্রয়োজনীয়তার সাথে মিল রেখে একটি কোয়েরি লিখতে পারি। উদাহরণ স্বরূপ:

WITH DistinctPotentialNonNullCustomers AS
(
    SELECT DISTINCT 
        PNC.cust_nbr 
    FROM #potentialNewCustomers AS PNC
    WHERE 
        PNC.cust_nbr IS NOT NULL
)
SELECT
    DPNNC.cust_nbr
FROM DistinctPotentialNonNullCustomers AS DPNNC
WHERE
    DPNNC.cust_nbr NOT IN
    (
        SELECT 
            EC.cust_nbr 
        FROM #existingCustomers AS EC 
        WHERE 
            EC.cust_nbr IS NOT NULL
    );

এটি একটি কার্যকর কার্যকর পরিকল্পনা তৈরি করে, যা সঠিক ফলাফল দেয়:

হত্যা পরিকল্পনা

আমরা পরিকল্পনা বা ফলাফলগুলিকে প্রভাবিত না করে বা NOT INহিসাবে প্রকাশ করতে পারি :<> ALLNOT = ANY

WITH DistinctPotentialNonNullCustomers AS
(
    SELECT DISTINCT 
        PNC.cust_nbr 
    FROM #potentialNewCustomers AS PNC
    WHERE 
        PNC.cust_nbr IS NOT NULL
)
SELECT
    DPNNC.cust_nbr
FROM DistinctPotentialNonNullCustomers AS DPNNC
WHERE
    DPNNC.cust_nbr <> ALL
    (
        SELECT 
            EC.cust_nbr 
        FROM #existingCustomers AS EC 
        WHERE 
            EC.cust_nbr IS NOT NULL
    );
WITH DistinctPotentialNonNullCustomers AS
(
    SELECT DISTINCT 
        PNC.cust_nbr 
    FROM #potentialNewCustomers AS PNC
    WHERE 
        PNC.cust_nbr IS NOT NULL
)
SELECT
    DPNNC.cust_nbr
FROM DistinctPotentialNonNullCustomers AS DPNNC
WHERE
    NOT DPNNC.cust_nbr = ANY
    (
        SELECT 
            EC.cust_nbr 
        FROM #existingCustomers AS EC 
        WHERE 
            EC.cust_nbr IS NOT NULL
    );

বা ব্যবহার NOT EXISTS:

WITH DistinctPotentialNonNullCustomers AS
(
    SELECT DISTINCT 
        PNC.cust_nbr 
    FROM #potentialNewCustomers AS PNC
    WHERE 
        PNC.cust_nbr IS NOT NULL
)
SELECT
    DPNNC.cust_nbr
FROM DistinctPotentialNonNullCustomers AS DPNNC
WHERE 
    NOT EXISTS
    (
        SELECT * 
        FROM #existingCustomers AS EC
        WHERE
            EC.cust_nbr = DPNNC.cust_nbr
            AND EC.cust_nbr IS NOT NULL
    );

কিছুই জাদু, এই সম্পর্কে নেই কিছু বিশেষ করে ব্যবহার সম্পর্কে আপত্তিকর IN, ANYঅথবা ALL- আমরা শুধু ক্যোয়ারী সঠিকভাবে লিখতে, তাই এটি সবসময় সঠিক ফলাফল উত্পাদন করা হবে প্রয়োজন।

সর্বাধিক কমপ্যাক্ট ফর্মটি ব্যবহার করে EXCEPT:

SELECT 
    PNC.cust_nbr 
FROM #potentialNewCustomers AS PNC
WHERE 
    PNC.cust_nbr IS NOT NULL
EXCEPT
SELECT
    EC.cust_nbr 
FROM #existingCustomers AS EC
WHERE 
    EC.cust_nbr IS NOT NULL;

এটি সঠিক ফলাফলও দেয়, যদিও বিটম্যাপ ফিল্টারিংয়ের অভাবে কার্যকরকরণ পরিকল্পনা কম দক্ষ হতে পারে:

নন-বিটম্যাপ বাস্তবায়ন পরিকল্পনা

মূল প্রশ্নটি আকর্ষণীয় কারণ এটি প্রয়োজনীয় নাল চেক প্রয়োগের সাথে কার্যকারিতা-প্রভাবিত সমস্যাটি প্রকাশ করে। এই উত্তরের বিষয়টি হ'ল ক্যোয়ারীটি সঠিকভাবে লিখলে সমস্যাটিও এড়ানো যায়।

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