কোনও বিস্তৃত সারণিতে একটি মাস্টার টেবিলে যোগদান করার সময়, আমি কীভাবে এসকিউএল সার্ভারকে সংযুক্ত আউটপুটের কার্ডিনালিটির প্রাক্কলন হিসাবে বৃহত্তর (বিশদ) সারণীর কার্ডিনালিটি অনুমানটি ব্যবহার করতে উত্সাহিত করতে পারি?
উদাহরণস্বরূপ, 10 কে মাস্টার সারিগুলিকে 100 কে বিশদ সারিগুলিতে যোগদান করার সময়, আমি এসকিউএল সার্ভারকে 100K সারিগুলিতে যোগদানের প্রাক্কলন করতে চাই - বিশদ সারিগুলির আনুমানিক সংখ্যার সমান। এসকিউএল সার্ভারের অনুমানকারীকে প্রতিটি বিশদ সারিতে সর্বদা অনুরূপ মাস্টার সারি থাকে তা উপকারে আসার জন্য আমার কীভাবে আমার প্রশ্নগুলি এবং / অথবা টেবিলগুলি এবং / অথবা সূচিগুলি গঠন করতে হবে? (এর অর্থ এই যে তাদের মধ্যে যোগদানের ফলে কার্ডিনালিটির প্রাক্কলনটি কখনই হ্রাস করা উচিত নয়))
আরও বিশদ এখানে। আমাদের ডাটাবেসটিতে সারণীর একটি মাস্টার / বিস্তারিত জোড় রয়েছে: VisitTarget
প্রতিটি বিক্রয় লেনদেনের VisitSale
জন্য একটি সারি থাকে এবং প্রতিটি লেনদেনে প্রতিটি পণ্যের জন্য একটি সারি থাকে। এটি এক থেকে একাধিক সম্পর্ক: গড়ে 10 টি ভিজিটসেল সারিগুলির জন্য একটি ভিজিটর্যাজেট সারি।
টেবিলগুলি দেখতে দেখতে: (আমি এই প্রশ্নের জন্য কেবলমাত্র প্রাসঙ্গিক কলামগুলিতেই সহজ করছি)
-- "master" table
CREATE TABLE VisitTarget
(
VisitTargetId int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
SaleDate date NOT NULL,
StoreId int NOT NULL
-- other columns omitted for clarity
);
-- covering index for date-scoped queries
CREATE NONCLUSTERED INDEX IX_VisitTarget_SaleDate
ON VisitTarget (SaleDate) INCLUDE (StoreId /*, ...more columns */);
-- "detail" table
CREATE TABLE VisitSale
(
VisitSaleId int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
VisitTargetId int NOT NULL,
SaleDate date NOT NULL, -- denormalized; copied from VisitTarget
StoreId int NOT NULL, -- denormalized; copied from VisitTarget
ItemId int NOT NULL,
SaleQty int NOT NULL,
SalePrice decimal(9,2) NOT NULL
-- other columns omitted for clarity
);
-- covering index for date-scoped queries
CREATE NONCLUSTERED INDEX IX_VisitSale_SaleDate
ON VisitSale (SaleDate)
INCLUDE (VisitTargetId, StoreId, ItemId, SaleQty, TotalSalePrice decimal(9,2) /*, ...more columns */
);
ALTER TABLE VisitSale
WITH CHECK ADD CONSTRAINT FK_VisitSale_VisitTargetId
FOREIGN KEY (VisitTargetId)
REFERENCES VisitTarget (VisitTargetId);
ALTER TABLE VisitSale
CHECK CONSTRAINT FK_VisitSale_VisitTargetId;
পারফরম্যান্সের কারণে, আমরা সর্বাধিক সাধারণ ফিল্টারিং কলামগুলি (যেমন SaleDate
) মাস্টার টেবিল থেকে প্রতিটি বিশদ টেবিল সারিগুলিতে অনুলিপি করেছি এবং তারপরে আমরা তারিখ-ফিল্টারযুক্ত প্রশ্নের আরও ভাল সমর্থন করার জন্য উভয় টেবিলের আচ্ছাদন সূচকগুলি যুক্ত করেছি। তারিখ-ফিল্টারযুক্ত ক্যোয়ারি চালানোর সময় এটি I / O হ্রাস করতে দুর্দান্ত কাজ করে তবে আমি মনে করি যে মাস্টার এবং বিশদ বিবরণ সারণিতে একসাথে যোগদানের সময় এই পদ্ধতির ফলে কার্ডিনালিটি অনুমানের সমস্যা হয়।
আমরা যখন এই দুটি টেবিলগুলিতে যোগদান করি তখন ক্যোয়ারীগুলি এর মতো দেখায়:
SELECT vt.StoreId, vt.SomeOtherColumn, Sales = sum(vs.SalePrice*vs.SaleQty)
FROM VisitTarget vt
JOIN VisitSale vs on vt.VisitTargetId = vs.VisitTargetId
WHERE
vs.SaleDate BETWEEN '20170101' and '20171231'
and vt.SaleDate BETWEEN '20170101' and '20171231'
-- more filtering goes here, e.g. by store, by product, etc.
বিশদ টেবিলের তারিখের ফিল্টার ( VisitSale
) অপ্রয়োজনীয়। এটি একটি তারিখের ব্যাপ্তি অনুসারে ফিল্টার করা প্রশ্নের জন্য বিশদ বিবরণ সারণিতে ক্রমানুসারে I / O (ওরফে সূচক সিক অপারেটর) সক্ষম করতে পারে।
এই ধরণের প্রশ্নের জন্য পরিকল্পনাটি এরকম দেখাচ্ছে:
একই সমস্যার সাথে একটি ক্যোয়ারির আসল পরিকল্পনা এখানে পাওয়া যাবে ।
আপনি দেখতে পাচ্ছেন, যোগদানের জন্য কার্ডিনালিটির অনুমান (ছবিতে নীচের বামে থাকা সরঞ্জামটিপ) খুব কম 4x এরও বেশি: 2.1 এম প্রকৃত বনাম 0.5 মি। এটি কর্মক্ষমতা সংক্রান্ত সমস্যার কারণ হয়ে দাঁড়ায় (যেমন টেম্পডিবির কাছে স্পিলিং), বিশেষত যখন এই ক্যোয়ারীটি এমন একটি সাব-কোয়ারী যা আরও জটিল কোয়েরিতে ব্যবহৃত হয়।
তবে যোগদানের প্রতিটি শাখার জন্য সারি গণনা অনুমানগুলি আসল সারি গণনার কাছাকাছি। যোগদানের উপরের অর্ধেকটি 100K আসল বনাম 164K আনুমানিক। যোগদানের নীচের অর্ধেকটি 2.1M সারি প্রকৃত বনাম 3.7 এম এর অনুমান। হ্যাশ বালতির বিতরণও ভাল দেখাচ্ছে। এই পর্যবেক্ষণগুলি আমার কাছে পরামর্শ দেয় যে পরিসংখ্যানগুলি প্রতিটি টেবিলের জন্য ঠিক আছে, এবং সমস্যাটি জয়েন্ট কার্ডিনালিটির অনুমান।
প্রথমে আমি ভেবেছিলাম যে সমস্যাটি এসকিউএল সার্ভারের প্রত্যাশা ছিল যে প্রতিটি টেবিলের সেলডেট কলামগুলি স্বতন্ত্র, যদিও সত্যই সেগুলি অভিন্ন। সুতরাং আমি বিক্রির তারিখগুলির সাথে যোগদানের শর্তে বা WHERE ধারাটিতে সমতা তুলনা যুক্ত করার চেষ্টা করেছি eg
ON vt.VisitTargetId = vs.VisitTargetId and vt.SaleDate = vs.SaleDate
অথবা
WHERE vt.SaleDate = vs.SaleDate
এটি কাজ করে না। এমনকি এটি কার্ডিনালিটির প্রাক্কলনগুলি আরও খারাপ করেছে! সুতরাং হয় এসকিউএল সার্ভার সেই সাম্যতার ইঙ্গিতটি ব্যবহার করছে না বা অন্য কিছু হ'ল সমস্যার মূল কারণ।
কীভাবে সমস্যা সমাধান করবেন এবং আশা রাখবেন যে এই কার্ডিনালিটি অনুমানের সমস্যাটি সমাধান করবেন? আমার লক্ষ্য হ'ল মাস্টার / ডিটেইল জয়েনের কার্ডিনালিটির জন্য যোগটির বৃহত্তর ("বিস্তৃত টেবিল") ইনপুটটির জন্য অনুমান হিসাবে একই অনুমান করা যায়।
যদি এটি গুরুত্বপূর্ণ হয় তবে আমরা উইন্ডোজ সার্ভারে এসকিউএল সার্ভার 2014 এন্টারপ্রাইজ এসপি 2 সিই 8 বিল্ড 12.0.5557.0 চালাচ্ছি। কোনও ট্রেস পতাকা সক্ষম নেই। ডেটাবেস সামঞ্জস্যতা স্তরটি এসকিউএল সার্ভার 2014। আমরা একাধিক বিভিন্ন এসকিউএল সার্ভারে একই আচরণ দেখতে পাই, তাই এটি কোনও সার্ভার-নির্দিষ্ট সমস্যা বলে মনে হয় না।
এসকিউএল সার্ভার ২০১৪ কার্ডিনালাইটি এসটিমেটরে একটি অপ্টিমাইজেশন রয়েছে যা আমি ঠিক আচরণ করে যাচ্ছি:
নতুন সিই, তবে একটি সহজ অ্যালগরিদম ব্যবহার করে যা ধরে নিয়েছে যে একটি বৃহত টেবিল এবং একটি ছোট টেবিলের মধ্যে একের মধ্যে অনেকে যোগ দেয়। এটি ধরে নেওয়া হয় যে বড় টেবিলের প্রতিটি সারি ছোট টেবিলের ঠিক এক সারিতে মেলে। এই অ্যালগরিদম যোগ কার্ডিনালটির হিসাবে বৃহত্তর ইনপুটটির আনুমানিক আকারটি দেয়।
আদর্শভাবে আমি এই আচরণটি পেতে পারি, যেখানে যোগদানের জন্য কার্ডিনালিটির প্রাক্কলনটি বড় টেবিলের জন্য অনুমানের সমান হবে, যদিও আমার "ছোট" টেবিলটি এখনও 100 কে সারি ধরে ফিরে আসবে!