"উপস্থিতি (…) বা বিদ্যমান (…)" এ ধারাগুলির ক্রম


11

আমার কাছে ক্লাসের ক্লাস রয়েছে যা দুটি জিনিসের মধ্যে একটির অস্তিত্বের জন্য পরীক্ষা করে। এটা ফর্ম হয়

SELECT CASE
  WHEN EXISTS (SELECT 1 FROM ...)
  OR EXISTS (SELECT 1 FROM ...)
THEN 1 ELSE 0 END;

আসল বিবৃতি সিতে উত্পন্ন হয় এবং একটি ওডিবিসি সংযোগের জন্য অ্যাড-হক ক্যোয়ারী হিসাবে কার্যকর করা হয়।

এটি সম্প্রতি প্রকাশিত হয়েছে যে দ্বিতীয় সেক্টর সম্ভবত বেশিরভাগ ক্ষেত্রে প্রথম নির্বাচন বাছাইয়ের চেয়ে দ্রুততর হবে এবং দুটি সন্ধানের ধারাটির ক্রমটি বদলে কমপক্ষে একটি আপত্তিজনক পরীক্ষার ক্ষেত্রে আমরা সবেমাত্র তৈরি করেছি।

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

(এটিও মনে হয় যদি এসকিউএল সার্ভারটি আরও চৌকস হয় তবে এটি সমান্তরালভাবে উভয় বিদ্যমান ধারাগুলি কার্যকর করে এবং যে কোনও একটি প্রথমে শর্ট সার্কিটটি সম্পন্ন করে let

এসকিউএল সার্ভারকে এই জাতীয় কোয়েরির চলমান সময়কে ধারাবাহিকভাবে উন্নত করার জন্য আরও ভাল উপায় কী?

হালনাগাদ

আপনার সময় এবং আমার প্রশ্নের আগ্রহের জন্য আপনাকে ধন্যবাদ। আমি প্রকৃত ক্যোয়ারী পরিকল্পনাগুলি সম্পর্কে প্রশ্নগুলির প্রত্যাশা করছিলাম না, তবে আমি সেগুলি ভাগ করতে রাজি আছি।

এটি এমন কোনও সফ্টওয়্যার উপাদানগুলির জন্য যা এসকিউএল সার্ভার 2008R2 এবং আরও বেশি সমর্থন করে। কনফিগারেশন এবং ব্যবহারের উপর নির্ভর করে ডেটার আকারটি বেশ আলাদা হতে পারে। আমার সহকর্মী ক্যোয়ারিতে এই পরিবর্তনটি করার কথা ভেবেছিলেন কারণ (উদাহরণস্বরূপ) dbf_1162761$z$rv$1257927703সারণীতে সর্বদা এতে dbf_1162761$z$dd$1257927703সারণির তুলনায় সারি সংখ্যার চেয়ে বড় বা সমান হবে - কখনও কখনও উল্লেখযোগ্য পরিমাণে আরও বেশি (প্রস্থের আদেশ)।

এখানে আমি আপত্তিজনক মামলা উল্লেখ করেছি। প্রথম ক্যোয়ারীটি ধীর এবং প্রায় 20 সেকেন্ড সময় নেয়। দ্বিতীয় ক্যোয়ারী তাত্ক্ষণিকভাবে সম্পূর্ণ হয়।

এটির মূল্যের জন্য, "অপটিমাইজ ফর আনকনউন" বিটটি সম্প্রতি যুক্ত করা হয়েছিল কারণ প্যারামিটার স্নিফিং কিছু নির্দিষ্ট ক্ষেত্রে ট্র্যাশ করছে।

মূল ক্যোয়ারী:

SELECT CASE
  WHEN EXISTS (SELECT 1 FROM zumero.dbf_1162761$z$rv$1257927703 rv INNER JOIN zumero.dbf_1162761$t$tx tx ON tx.txid=rv.txid WHERE tx.generation BETWEEN 1500 AND 2502)
  OR EXISTS (SELECT 1 FROM zumero.dbf_1162761$z$dd$1257927703 dd INNER JOIN zumero.dbf_1162761$t$tx tx ON tx.txid=dd.txid WHERE tx.generation BETWEEN 1500 AND 2502)
THEN 1 ELSE 0 END
OPTION (OPTIMIZE FOR UNKNOWN)

মূল পরিকল্পনা:

|--Compute Scalar(DEFINE:([Expr1006]=CASE WHEN [Expr1007] THEN (1) ELSE (0) END))
     |--Nested Loops(Left Semi Join, DEFINE:([Expr1007] = [PROBE VALUE]))
          |--Constant Scan
          |--Concatenation
               |--Nested Loops(Inner Join, WHERE:([scale].[zumero].[dbf_1162761$z$rv$1257927703].[txid] as [rv].[txid]=[scale].[zumero].[dbf_1162761$t$tx].[txid] as [tx].[txid]))
               |    |--Clustered Index Scan(OBJECT:([scale].[zumero].[dbf_1162761$z$rv$1257927703].[PK__dbf_1162__97770A2F62EEAE79] AS [rv]), WHERE:([scale].[zumero].[dbf_1162761$z$rv$1257927703].[txid] as [rv].[txid]>(0)))
               |    |--Index Seek(OBJECT:([scale].[zumero].[dbf_1162761$t$tx].[gendex] AS [tx]), SEEK:([tx].[generation] >= (1500) AND [tx].[generation] <= (2502)) ORDERED FORWARD)
               |--Nested Loops(Inner Join, OUTER REFERENCES:([tx].[txid]))
                    |--Clustered Index Scan(OBJECT:([scale].[zumero].[dbf_1162761$t$tx].[PK__dbf_1162__E3BA953EC2197789] AS [tx]),  WHERE:([scale].[zumero].[dbf_1162761$t$tx].[generation] as [tx].[generation]>=(1500) AND [scale].[zumero].[dbf_1162761$t$tx].[generation] as [tx].[generation]<=(2502)) ORDERED FORWARD)
                    |--Index Seek(OBJECT:([scale].[zumero].[dbf_1162761$z$dd$1257927703].[n$dbf_1162761$z$dd$txid$1257927703] AS [dd]), SEEK:([dd].[txid]=[scale].[zumero].[dbf_1162761$t$tx].[txid] as [tx].[txid]),  WHERE:([scale].[zumero].[dbf_1162761$z$dd$1257927703].[txid] as [dd].[txid]>(0)) ORDERED FORWARD)

স্থির ক্যোয়ারী:

SELECT CASE
  WHEN EXISTS (SELECT 1 FROM zumero.dbf_1162761$z$dd$1257927703 dd INNER JOIN zumero.dbf_1162761$t$tx tx ON tx.txid=dd.txid WHERE tx.generation BETWEEN 1500 AND 2502)
  OR EXISTS (SELECT 1 FROM zumero.dbf_1162761$z$rv$1257927703 rv INNER JOIN zumero.dbf_1162761$t$tx tx ON tx.txid=rv.txid WHERE tx.generation BETWEEN 1500 AND 2502)
THEN 1 ELSE 0 END
OPTION (OPTIMIZE FOR UNKNOWN)

স্থির পরিকল্পনা:

|--Compute Scalar(DEFINE:([Expr1006]=CASE WHEN [Expr1007] THEN (1) ELSE (0) END))
     |--Nested Loops(Left Semi Join, DEFINE:([Expr1007] = [PROBE VALUE]))
          |--Constant Scan
          |--Concatenation
               |--Nested Loops(Inner Join, OUTER REFERENCES:([tx].[txid]))
               |    |--Clustered Index Scan(OBJECT:([scale].[zumero].[dbf_1162761$t$tx].[PK__dbf_1162__E3BA953EC2197789] AS [tx]),  WHERE:([scale].[zumero].[dbf_1162761$t$tx].[generation] as [tx].[generation]>=(1500) AND [scale].[zumero].[dbf_1162761$t$tx].[generation] as [tx].[generation]<=(2502)) ORDERED FORWARD)
               |    |--Index Seek(OBJECT:([scale].[zumero].[dbf_1162761$z$dd$1257927703].[n$dbf_1162761$z$dd$txid$1257927703] AS [dd]), SEEK:([dd].[txid]=[scale].[zumero].[dbf_1162761$t$tx].[txid] as [tx].[txid]),  WHERE:([scale].[zumero].[dbf_1162761$z$dd$1257927703].[txid] as [dd].[txid]>(0)) ORDERED FORWARD)
               |--Nested Loops(Inner Join, WHERE:([scale].[zumero].[dbf_1162761$z$rv$1257927703].[txid] as [rv].[txid]=[scale].[zumero].[dbf_1162761$t$tx].[txid] as [tx].[txid]))
                    |--Clustered Index Scan(OBJECT:([scale].[zumero].[dbf_1162761$z$rv$1257927703].[PK__dbf_1162__97770A2F62EEAE79] AS [rv]), WHERE:([scale].[zumero].[dbf_1162761$z$rv$1257927703].[txid] as [rv].[txid]>(0)))
                    |--Index Seek(OBJECT:([scale].[zumero].[dbf_1162761$t$tx].[gendex] AS [tx]), SEEK:([tx].[generation] >= (1500) AND [tx].[generation] <= (2502)) ORDERED FORWARD)

উত্তর:


11

থাম্বের একটি সাধারণ নিয়ম হিসাবে, এসকিউএল সার্ভার একটি CASEবিবৃতিটির অংশগুলি যথাযথভাবে কার্যকর করবে তবে ORশর্তগুলি পুনরায় অর্ডার করতে বিনামূল্যে । কিছু প্রশ্নের জন্য আপনি WHENএকটি CASEবিবৃতিতে প্রকাশের ক্রমটি পরিবর্তন করে ধারাবাহিকভাবে আরও ভাল পারফরম্যান্স পেতে পারেন । কোনও ORবিবৃতিতে শর্তের ক্রম পরিবর্তন করার সময় কখনও কখনও আপনি আরও ভাল পারফরম্যান্স পেতে পারেন তবে এটি গ্যারান্টিযুক্ত আচরণ নয়।

একটি সাধারণ উদাহরণ সহ এটি দিয়ে চলাই সম্ভবত সেরা। আমি এসকিউএল সার্ভার ২০১ against এর বিপরীতে পরীক্ষা করছি তাই এটি সম্ভব যে আপনি আপনার মেশিনে ঠিক একই ফলাফল পাবেন না, তবে যতদূর আমি জানি একই নীতিগুলি প্রয়োগ হয়। প্রথমে আমি দুটি টেবিলের মধ্যে 1 থেকে 1000000 পর্যন্ত দশ মিলিয়ন পূর্ণসংখ্যা স্থাপন করব, একটি ক্লাস্টার ইনডেক্সযুক্ত এবং একটি গাদা হিসাবে:

CREATE TABLE dbo.X_HEAP (ID INT NOT NULL, FLUFF VARCHAR(100));

INSERT INTO dbo.X_HEAP  WITH (TABLOCK)
SELECT TOP (1000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), REPLICATE('Z', 100)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);

CREATE TABLE dbo.X_CI (ID INT NOT NULL, FLUFF VARCHAR(100), PRIMARY KEY (ID));

INSERT INTO dbo.X_CI  WITH (TABLOCK)
SELECT TOP (1000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), REPLICATE('Z', 100)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);

নিম্নলিখিত কোয়েরি বিবেচনা করুন:

SELECT CASE
  WHEN EXISTS (SELECT 1 FROM dbo.X_HEAP WHERE ID = 500000)
  OR EXISTS (SELECT 1 FROM dbo.X_CI WHERE ID = 500000)
THEN 1 ELSE 0 END;

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

এই উদাহরণস্বরূপ ডেটার জন্য আমি কোয়েরিটি এভাবে লিখব:

SELECT CASE
  WHEN EXISTS (SELECT 1 FROM dbo.X_CI WHERE ID = 500000) THEN 1 
  WHEN EXISTS (SELECT 1 FROM dbo.X_HEAP WHERE ID = 500000) THEN 1 
ELSE 0 END;

এটি কার্যকরভাবে এসকিউএল সার্ভারকে প্রথমে ক্লাস্টারড ইনডেক্সের সাহায্যে টেবিলের বিপরীতে সাবকিউরি চালাতে বাধ্য করে। এখানে থেকে ফলাফলগুলি SET STATISTICS IO, TIME ON:

সারণী 'এক্স_সিআই'। স্ক্যান কাউন্ট 0, লজিকাল রিড 3, ফিজিকাল রিড 0

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

ক্যোয়ারী পরিকল্পনার দিকে তাকানো, যদি লেবেল 1 এ সন্ধানটি লেবেল 2 এ স্ক্যানের চেয়ে কোনও ডেটা ফেরত দেয় তবে তা ঘটে না:

ভাল জিজ্ঞাসা

নিম্নলিখিত কোয়েরিটি অনেক কম দক্ষ:

SELECT CASE
  WHEN EXISTS (SELECT 1 FROM dbo.X_HEAP WHERE ID = 500000) THEN 1 
  WHEN EXISTS (SELECT 1 FROM dbo.X_CI WHERE ID = 500000) THEN 1 
ELSE 0 END
OPTION (MAXDOP 1);

ক্যোয়ারী পরিকল্পনাটি দেখে আমরা দেখতে পাচ্ছি যে লেবেল 2 এ স্ক্যানটি সর্বদা ঘটে। যদি একটি সারি পাওয়া যায় তবে 1 লেবেলের সন্ধানটি বাদ দেওয়া হয়। আমরা যে আদেশটি চেয়েছিলাম সেটি নয়:

খারাপ জিজ্ঞাসা পরিকল্পনা

কর্মক্ষমতা ফলাফল যে ফিরে:

সারণী 'এক্স_এইপি'। স্ক্যান কাউন্ট 1, যৌক্তিক পাঠ্য 7247

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

মূল ক্যোয়ারিতে ফিরে যাওয়া, এই ক্যোয়ারির জন্য আমি সন্ধান এবং স্ক্যানটি ক্রিয়াকলাপের পক্ষে ভাল ক্রমে মূল্যায়ন করে দেখছি:

SELECT CASE
  WHEN EXISTS (SELECT 1 FROM dbo.X_HEAP WHERE ID = 500000)
  OR EXISTS (SELECT 1 FROM dbo.X_CI WHERE ID = 500000)
THEN 1 ELSE 0 END;

এবং এই ক্যোয়ারিতে এগুলি বিপরীত ক্রমে মূল্যায়ন করা হয়:

SELECT CASE
  WHEN EXISTS (SELECT 1 FROM dbo.X_CI WHERE ID = 500000)
  OR EXISTS (SELECT 1 FROM dbo.X_HEAP WHERE ID = 500000)
THEN 1 ELSE 0 END;

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

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

সংযোজন:

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

এখানে একটি বিকল্প যা সাধারণ ডেমো টেবিলগুলির জন্য কাজ করে বলে মনে হচ্ছে:

SELECT CASE
  WHEN EXISTS (
    SELECT 1
    FROM (
        SELECT TOP 2 1 t
        FROM 
        (
            SELECT 1 ID

            UNION ALL

            SELECT TOP 1 ID 
            FROM dbo.X_HEAP 
            WHERE ID = 50000 
        ) h
        CROSS JOIN
        (
            SELECT 1 ID

            UNION ALL

            SELECT TOP 1 ID 
            FROM dbo.X_CI
            WHERE ID = 50000
        ) ci
    ) cnt
    HAVING COUNT(*) = 2
)
THEN 1 ELSE 0 END;

আপনি এখানে একটি ডিবি ফিডাল ডেমো খুঁজে পেতে পারেন । উত্পন্ন টেবিলগুলির ক্রম পরিবর্তন করা কোয়েরি প্ল্যান পরিবর্তন করে না। উভয় প্রশ্নের মধ্যে X_HEAPটেবিলটি স্পর্শ করা হয়নি। অন্য কথায়, ক্যোয়ারী অপ্টিমাইজারটি প্রথমে সস্তা ক্যোয়ারিকে কার্যকর করতে উপস্থিত হয়। আমি উত্পাদনে এ জাতীয় কিছু ব্যবহার করার পরামর্শ দিতে পারি না তাই এটি বেশিরভাগ কৌতূহল মানের জন্য এখানে। একই জিনিসটি সম্পাদন করার জন্য আরও অনেক সহজ উপায় হতে পারে।


4
বা CASE WHEN EXISTS (SELECT 1 FROM dbo.X_CI WHERE ID = 500000 UNION ALL SELECT 1 FROM dbo.X_HEAP WHERE ID = 500000) THEN 1 ELSE 0 ENDবিকল্প হতে পারে, যদিও এটি এখনও কোন ক্যোয়ারীটি দ্রুততর তা নিজেই সিদ্ধান্ত নেওয়ার এবং সেইটিকে প্রথমে রাখার উপর নির্ভর করে putting আমি নিশ্চিত নই যে এটি প্রকাশ করার কোনও উপায় আছে যাতে এসকিউএল সার্ভারটি স্বয়ংক্রিয়ভাবে পুনঃক্রম হবে যাতে সস্তাটি স্বয়ংক্রিয়ভাবে প্রথমে মূল্যায়ন হয়।
মার্টিন স্মিথ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.