এটি একটি বিষয় যা আমি পর্যায়ক্রমে বিরুদ্ধে এসেছি এবং এর পক্ষে এখনও ভাল সমাধান খুঁজে পাইনি।
নিম্নলিখিত সারণির কাঠামোটি মনে করি
CREATE TABLE T
(
A INT PRIMARY KEY,
B CHAR(1000) NULL,
C CHAR(1000) NULL
)
এবং প্রয়োজনীয়তাটি হ'ল নালামযোগ্য কলামগুলির মধ্যে কোনওটি নির্ধারণ করা উচিত Bবা Cআসলে কোনও NULLমান রয়েছে (এবং যদি তাই হয় তবে কোনটি (গুলি))।
এছাড়াও ধরে নিন যে টেবিলটিতে কয়েক মিলিয়ন সারি রয়েছে (এবং এই স্তরের ক্যোয়ারির আরও জেনেরিক সমাধানের জন্য আমি আগ্রহী বলে কোনও কলামের পরিসংখ্যান পাওয়া যায়নি যে তা উঁকি দেওয়া যায়)।
আমি এটি কাছে আসার কয়েকটি উপায় সম্পর্কে ভাবতে পারি তবে সবগুলিরই দুর্বলতা রয়েছে।
দুটি পৃথক EXISTSবিবৃতি। এই বন্ধ স্ক্যানিং শব্দতে গোড়ার দিকে যত তাড়াতাড়ি একটি সক্ষম হবেন সুবিধা হবে NULLপাওয়া যায়। তবে যদি উভয় কলামে বাস্তবে কোনও NULLগুলি থাকে তবে দুটি পূর্ণ স্ক্যানের ফলাফল হবে।
একক সমষ্টি অনুসন্ধান
SELECT
MAX(CASE WHEN B IS NULL THEN 1 ELSE 0 END) AS B,
MAX(CASE WHEN C IS NULL THEN 1 ELSE 0 END) AS C
FROM T
এটি একই সাথে উভয় কলামে প্রক্রিয়া করতে পারে সুতরাং একটি পূর্ণ স্ক্যানের সবচেয়ে খারাপ ক্ষেত্রে। অসুবিধাটি হ'ল এমনকি যদি এটি NULLখুব প্রথম দিকে উভয় কলামে মুখোমুখি হয় তবে এখনও টেবিলের বাকী পুরো অংশটি স্ক্যান করে শেষ করা হবে।
ব্যবহারকারী ভেরিয়েবল
আমি এটি করার একটি তৃতীয় উপায় সম্পর্কে ভাবতে পারি
BEGIN TRY
DECLARE @B INT, @C INT, @D INT
SELECT
@B = CASE WHEN B IS NULL THEN 1 ELSE @B END,
@C = CASE WHEN C IS NULL THEN 1 ELSE @C END,
/*Divide by zero error if both @B and @C are 1.
Might happen next row as no guarantee of order of
assignments*/
@D = 1 / (2 - (@B + @C))
FROM T
OPTION (MAXDOP 1)
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 8134 /*Divide by zero*/
BEGIN
SELECT 'B,C both contain NULLs'
RETURN;
END
ELSE
RETURN;
END CATCH
SELECT ISNULL(@B,0),
ISNULL(@C,0)
তবে এটি প্রোডাকশন কোডের জন্য উপযুক্ত নয় কারণ সামগ্রিক উপসংহার প্রশ্নের জন্য সঠিক আচরণ অপরিজ্ঞাত। এবং ত্রুটি ছুড়ে দিয়ে স্ক্যানটি সমাপ্ত করা যাইহোক, বেশ ভয়ঙ্কর সমাধান।
উপরোক্ত পদ্ধতির শক্তিকে একত্রিত করে এমন কি আরও একটি বিকল্প রয়েছে?
সম্পাদন করা
কেবলমাত্র এ পর্যন্ত জমা দেওয়া উত্তরের জন্য পড়ার পরিপ্রেক্ষিতে প্রাপ্ত ফলাফলের সাথে এটি আপডেট করতে (@ ইয়পারকিউবের পরীক্ষার ডেটা ব্যবহার করে)
+----------+------------+------+---------+----------+----------------------+----------+------------------+
| | 2 * EXISTS | CASE | Kejser | Kejser | Kejser | ypercube | 8kb |
+----------+------------+------+---------+----------+----------------------+----------+------------------+
| | | | | MAXDOP 1 | HASH GROUP, MAXDOP 1 | | |
| No Nulls | 15208 | 7604 | 8343 | 7604 | 7604 | 15208 | 8346 (8343+3) |
| One Null | 7613 | 7604 | 8343 | 7604 | 7604 | 7620 | 7630 (25+7602+3) |
| Two Null | 23 | 7604 | 8343 | 7604 | 7604 | 30 | 30 (18+12) |
+----------+------------+------+---------+----------+----------------------+----------+------------------+
@ থমাস এর উত্তর আমি পরিবর্তন জন্য TOP 3করতে TOP 2আগেই থেকে প্রস্থান করার জন্য সম্ভাব্য অনুমতি দেয়। আমি উত্তরটির জন্য পূর্বনির্ধারিতভাবে একটি সমান্তরাল পরিকল্পনা পেয়েছি তাই MAXDOP 1অন্যান্য পরিকল্পনার সাথে পাঠ্যের সংখ্যা আরও তুলনীয় করার জন্য একটি ইঙ্গিত দিয়ে চেষ্টা করেছি । আমার আগের পরীক্ষার মতো ফলাফলগুলি দেখে আমি কিছুটা অবাক হয়েছিলাম পুরো টেবিলটি না পড়ে query কোয়েরি শর্ট সার্কিটটি দেখেছি।
শর্ট সার্কিটগুলির নীচে আমার পরীক্ষার ডেটার জন্য পরিকল্পনা

ইপারকিউবের ডেটার পরিকল্পনা রয়েছে

সুতরাং এটি পরিকল্পনায় একটি ব্লকিং বাছাই করা অপারেটর যুক্ত করে। আমি HASH GROUPইঙ্গিত দিয়ে চেষ্টাও করেছি তবে এটি এখনও সমস্ত সারিটি পড়া শেষ করে

সুতরাং মূল hash match (flow distinct)বিকল্পটি অন্য বিকল্পগুলি যে কোনওভাবে ব্লক করে এবং সমস্ত সারি গ্রাস করবে বলে শর্ট সার্কিটের জন্য এই পরিকল্পনার অনুমতি দেওয়ার জন্য কোনও অপারেটরকে পাওয়া উচিত বলে মনে হয় । আমি মনে করি না এটি নির্দিষ্টভাবে জোর করার কোনও ইঙ্গিত রয়েছে তবে স্পষ্টতই "সাধারণভাবে, অপটিমাইজার একটি ফ্লো ডিসস্টিন্ট বেছে নেয় যেখানে এটি নির্ধারণ করে যে ইনপুট সেটে স্বতন্ত্র মানগুলির চেয়ে কম আউটপুট সারি প্রয়োজন are" ।
@ ইপারকিউবের ডেটাতে প্রতিটি কলামে NULLমানগুলি (সারণী কার্ডিনালিটি = 30300) সহ কেবল 1 টি সারি থাকে এবং অপারেটরের ভিতরে এবং বাইরে যাওয়া আনুমানিক সারি উভয়ই 1। শিকারটিকে অপটিমাইজারের কাছে আরও কিছুটা অস্বচ্ছ করে ফ্লো ডিস্টিন্ট অপারেটরের সাথে একটি পরিকল্পনা তৈরি করে।
SELECT TOP 2 *
FROM (SELECT DISTINCT
CASE WHEN b IS NULL THEN NULL ELSE 'foo' END AS b
, CASE WHEN c IS NULL THEN NULL ELSE 'bar' END AS c
FROM test T
WHERE LEFT(b,1) + LEFT(c,1) IS NULL
) AS DT
সম্পাদনা 2
আমার কাছে একটি সর্বশেষ টুইট হয়েছে যে উপরের ক্যোয়ারিতে প্রয়োজনীয়তার চেয়ে আরও বেশি সারি প্রক্রিয়াজাতকরণ শেষ হতে পারে যদি এটির সাথে প্রথম সারির মুখোমুখি হয় তবে NULLকলাম Bএবং উভয় ক্ষেত্রেই ন্যূনাল রয়েছে C। এটি তাত্ক্ষণিকভাবে বেরিয়ে আসার চেয়ে স্ক্যান করা চালিয়ে যাবে। এটি এড়ানোর একটি উপায় হ'ল সারিগুলি স্ক্যান করার সাথে সাথে তা সরিয়ে ফেলা হবে। সুতরাং টমাস কেজারের উত্তরে আমার চূড়ান্ত সংশোধন নীচে রয়েছে
SELECT DISTINCT TOP 2 NullExists
FROM test T
CROSS APPLY (VALUES(CASE WHEN b IS NULL THEN 'b' END),
(CASE WHEN c IS NULL THEN 'c' END)) V(NullExists)
WHERE NullExists IS NOT NULL
ভবিষ্যদ্বাণী করাটি সম্ভবত এটির চেয়ে ভাল WHERE (b IS NULL OR c IS NULL) AND NullExists IS NOT NULLতবে আগের পরীক্ষার ডেটার বিপরীতে যে আমাকে ফ্লো ডিসস্টিন্টের সাথে কোনও পরিকল্পনা দেয় না, যেখানে NullExists IS NOT NULLএকটি করে (নীচে পরিকল্পনা করে)।

TOP 3মাত্র হতে পারেTOP 2না হওয়া পর্যন্ত নিম্নলিখিত প্রতিটি এক খুঁজে বের এটা স্ক্যান করবে বর্তমানে(NOT_NULL,NULL),(NULL,NOT_NULL),(NULL,NULL)। এই 3 টির মধ্যে 2 টিও যথেষ্ট হবে - এবং এটি যদি(NULL,NULL)প্রথমটি খুঁজে পায় তবে দ্বিতীয়টিরও প্রয়োজন হবে না। এছাড়াও শর্ট সার্কিট করার জন্য পরিকল্পনা একটি মাধ্যমে স্বতন্ত্র বাস্তবায়ন করতে হবেhash match (flow distinct)বরং অপারেটরhash match (aggregate)বাdistinct sort