আমি এই প্রযুক্তিটি অন্যদের সাথে কীভাবে তুলনা করব তা দেখার জন্য আমি কিছুটা ভিন্ন পন্থা নিয়েছিলাম, কারণ বিকল্পগুলি থাকা ভাল, তাই না?
টেস্টিং
আমরা কীভাবে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে একে অপামের বিপরীতে টানতে চাই না। " আমি তিনটি সেট পরীক্ষা করেছি:
- প্রথম সেটটি কোনও ডিবি পরিবর্তন ছাড়াই চলে
- দ্বিতীয় সেটটি
TransactionDate
বিরোধী-ভিত্তিক প্রশ্নের সমর্থনে একটি সূচক তৈরি হওয়ার পরে চলেছিল Production.TransactionHistory
।
- তৃতীয় সেটটি কিছুটা আলাদা ধারণা নিয়েছে। যেহেতু তিনটি পরীক্ষাই একই পণ্যের তালিকার বিরুদ্ধে ছিল, আমরা যদি সেই তালিকাটি ক্যাশে করি তবে কী হবে? আমার পদ্ধতিটি ইন-মেমরি ক্যাশে ব্যবহার করে অন্য পদ্ধতিগুলি সমমানের টেম্প টেবিল ব্যবহার করে। দ্বিতীয় সেট পরীক্ষার জন্য তৈরি সহায়ক সূচকটি এই পরীক্ষার সেটগুলির জন্য এখনও বিদ্যমান।
অতিরিক্ত পরীক্ষার বিবরণ:
AdventureWorks2012
এসকিউএল সার্ভার ২০১২, এসপি 2 (বিকাশকারী সংস্করণ) এ পরীক্ষা করা হয়েছিল ।
- প্রতিটি পরীক্ষার জন্য আমি কার উত্তরটি দিয়েছিলাম এবং এটি কোন বিশেষ কোয়েরি থেকে লেবেল দিয়েছিল।
- আমি অনুসন্ধানের বিকল্পগুলির "মৃত্যুর পরে ফলাফলগুলি বাতিল করুন" বিকল্পটি ব্যবহার করেছি used ফলাফল।
- Please note that for the first two sets of tests, the
RowCounts
appear to be "off" for my method. This is due my method being a manual implementation of what CROSS APPLY
is doing: it runs the initial query against Production.Product
and gets 161 rows back, which it then uses for the queries against Production.TransactionHistory
. Hence, the RowCount
values for my entries are always 161 more than the other entries. In the third set of tests (with caching) the row counts are the same for all methods.
- আমি এসকিউএল সার্ভার প্রোফাইলারকে কার্যকর করার পরিকল্পনার উপর নির্ভর না করে স্ট্যাটাস ক্যাপচার করতে ব্যবহার করি। হারুন এবং মিকেল ইতিমধ্যে তাদের প্রশ্নের পরিকল্পনাগুলি দেখিয়ে একটি দুর্দান্ত কাজ করেছেন এবং সেই তথ্যটি পুনরুত্পাদন করার দরকার নেই। এবং আমার পদ্ধতির উদ্দেশ্যটি হল এমন প্রশ্নগুলিকে এমন একটি সহজ আকারে হ্রাস করা যা এটি সত্যিই গুরুত্ব দেয় না। প্রোফাইলার ব্যবহারের অতিরিক্ত কারণ রয়েছে তবে পরে তা উল্লেখ করা হবে।
Name >= N'M' AND Name < N'S'
কনস্ট্রাক্টটি ব্যবহার করার পরিবর্তে , আমি ব্যবহার করতে বেছে নিয়েছি Name LIKE N'[M-R]%'
এবং এসকিউএল সার্ভার তাদের সাথে একই আচরণ করে।
ফলাফলগুলো
কোনও সমর্থন সূচক নেই
এটি মূলত অ্যাডভেঞ্চার ওয়ার্কস ২০১২-এর বাইরে is সব ক্ষেত্রেই আমার পদ্ধতিটি অন্য কয়েকটির থেকে পরিষ্কারভাবে ভাল তবে শীর্ষ 1 বা 2 পদ্ধতির মতো কখনও ভাল নয়।
টেস্ট 1
অ্যারনের সিটিই স্পষ্টতই এখানে বিজয়ী।
টেস্ট 2
হারুনের সিটিই (আবার) এবং মিকেলের দ্বিতীয় apply row_number()
পদ্ধতিটি নিকটবর্তী দ্বিতীয়।
টেস্ট 3
হারুনের সিটিই (আবার) বিজয়ী।
উপসংহার
যখন কোনও সমর্থনকারী সূচক চালু নেই TransactionDate
, তখন আমার পদ্ধতিটি একটি স্ট্যান্ডার্ড করার চেয়ে ভাল CROSS APPLY
তবে তবুও, সিটিই পদ্ধতিটি ব্যবহার করা সুস্পষ্টভাবে যাওয়ার উপায়।
সমর্থন সূচক সহ (কোনও ক্যাচিং নেই)
পরীক্ষার এই সেটটির জন্য আমি TransactionHistory.TransactionDate
সেই ক্ষেত্রটিতে সমস্ত প্রশ্নের ক্রম সাজানোর কারণে সুস্পষ্ট সূচকটি যুক্ত করেছি । আমি বলি "সুস্পষ্ট" যেহেতু অন্যান্য উত্তরগুলিও এই বিষয়ে একমত হয়। এবং যেহেতু অনুসন্ধানগুলি সকলেই সর্বাধিক সাম্প্রতিক তারিখগুলি চাইছে, তাই TransactionDate
ক্ষেত্রটি অর্ডার করা উচিত DESC
, তাই আমি কেবল CREATE INDEX
মিকারেলের উত্তরের নীচে বিবৃতিটি ধরলাম এবং একটি স্পষ্টত যুক্ত করেছি FILLFACTOR
:
CREATE INDEX [IX_TransactionHistoryX]
ON Production.TransactionHistory (ProductID ASC, TransactionDate DESC)
WITH (FILLFACTOR = 100);
এই সূচকটি একবার এলে ফলাফল কিছুটা বদলে যায় change
পরীক্ষা 1
এই বারটি আমার পদ্ধতি যা সামনে আসে, অন্তত লজিকাল রিডসের ক্ষেত্রে। CROSS APPLY
পদ্ধতি, পূর্বে টেস্ট 1 খারাপ অভিনয়কারী, স্থিতিকাল ময়দানে জিতেছে এবং এমনকি কোটে পদ্ধতি beats লজিক্যাল রাউন্ডআপ উপর।
টেস্ট 2
এবার মিকেলের প্রথম apply row_number()
পদ্ধতি হ'ল রিডস দেখার সময় এটিই বিজয়ী, যদিও এর আগে এটি সবচেয়ে খারাপ অভিনেতা ছিল। আর এখন আমার পদ্ধতিটি রিডস দেখার সময় খুব কাছাকাছি দ্বিতীয় স্থানে চলে আসে। আসলে, সিটিই পদ্ধতির বাইরে, বাকিগুলি সমস্তই রিডসের ক্ষেত্রে মোটামুটি কাছাকাছি।
টেস্ট 3
এখানে সিটিই এখনও বিজয়ী, তবে এখন সূচি তৈরির আগে বিদ্যমান তাত্পর্যপূর্ণ পার্থক্যের তুলনায় অন্যান্য পদ্ধতির মধ্যে পার্থক্য সবেমাত্র লক্ষণীয়।
উপসংহার
আমার পদ্ধতির প্রয়োগযোগ্যতা এখন আরও স্পষ্ট, যদিও যথাযথ সূচী স্থানে না রাখার ক্ষেত্রে এটি কম স্বচ্ছন্দ।
সমর্থন সূচক এবং ক্যাচিং সহ
পরীক্ষার এই সেটটির জন্য আমি ক্যাশে ব্যবহার করেছি কারণ, ভাল, কেন না? আমার পদ্ধতিটি ইন-মেমরি ক্যাশে ব্যবহারের অনুমতি দেয় যা অন্যান্য পদ্ধতি অ্যাক্সেস করতে পারে না। সুতরাং ন্যায়বিচারের জন্য, আমি নিম্নলিখিত টেম্প টেবিলটি তৈরি করেছি যা Product.Product
তিনটি পরীক্ষার জুড়ে other অন্যান্য পদ্ধতিতে সমস্ত রেফারেন্সের জায়গায় ব্যবহার করা হয়েছিল । DaysToManufacture
ক্ষেত্রটি কেবল টেস্ট নম্বর 2 এ ব্যবহৃত হয়, তবে একই টেবিলটি ব্যবহার করার জন্য এসকিউএল স্ক্রিপ্টগুলির মধ্যে সামঞ্জস্য থাকা আরও সহজ ছিল এবং এটি এটির জন্য ক্ষতি করে নি।
CREATE TABLE #Products
(
ProductID INT NOT NULL PRIMARY KEY,
Name NVARCHAR(50) NOT NULL,
DaysToManufacture INT NOT NULL
);
INSERT INTO #Products (ProductID, Name, DaysToManufacture)
SELECT p.ProductID, p.Name, p.DaysToManufacture
FROM Production.Product p
WHERE p.Name >= N'M' AND p.Name < N'S'
AND EXISTS (
SELECT *
FROM Production.TransactionHistory th
WHERE th.ProductID = p.ProductID
);
ALTER TABLE #Products REBUILD WITH (FILLFACTOR = 100);
পরীক্ষা 1
সমস্ত পদ্ধতি ক্যাশে থেকে সমানভাবে উপকৃত হবে বলে মনে হয় এবং এখনও আমার পদ্ধতিটি সামনে আসে।
পরীক্ষা 2
এখানে এখন আমি লাইনআপের মধ্যে একটি পার্থক্য দেখতে পাচ্ছি যেহেতু আমার পদ্ধতিটি সবে সামনে এগিয়ে আসে, মিকেলের প্রথম apply row_number()
পদ্ধতির চেয়ে কেবল 2 টি আরও ভাল পড়েন , যখন ক্যাচিং ছাড়াই আমার পদ্ধতিটি 4 টি পড়ার পিছনে ছিল।
পরীক্ষা 3
দয়া করে নীচের দিকে (লাইনের নীচে) আপডেট দেখুন । এখানে আমরা আবার কিছু পার্থক্য দেখতে পাই। আমার পদ্ধতির "প্যারামিটারাইজড" গন্ধটি এখন অ্যারনের ক্রস অ্যাপ্লাই পদ্ধতির তুলনায় 2 রিডের তুলনায় সবেমাত্র নেতৃত্বে রয়েছে (কোনও ক্যাশে ছাড়াই তারা সমান ছিল না)। তবে সত্যিই আশ্চর্যের বিষয় হ'ল প্রথমবারের মতো আমরা এমন একটি পদ্ধতি দেখি যা ক্যাচিংয়ের মাধ্যমে নেতিবাচকভাবে প্রভাবিত হয়: হারুনের সিটিই পদ্ধতি (যা আগে টেস্ট নম্বর 3 এর জন্য সেরা ছিল)। তবে, যেখানে creditণ নেই সেখানে আমি ক্রেডিট নিতে যাচ্ছি না এবং যেহেতু ক্যাচিং ছাড়াই হারুনের সিটিই পদ্ধতিটি আমার পদ্ধতিটি এখানে ক্যাচিংয়ের তুলনায় আরও দ্রুত, তাই এই নির্দিষ্ট পরিস্থিতির জন্য সেরা পদ্ধতির উপস্থিতি হারুনের সিটিই পদ্ধতি হিসাবে দেখা যায়।
উপসংহার দয়া করে নীচের দিকে আপডেটটি দেখুন (রেখার নীচে) এমন
পরিস্থিতি যা একটি গৌণ ক্যোয়ারির ফলাফলগুলির বারবার ব্যবহার করে সেগুলি প্রায়শই (তবে সর্বদা নয়) এই ফলাফলগুলি ক্যাশে করে উপকার পেতে পারে। তবে যখন ক্যাচিং একটি সুবিধা হয় তখন মেমরি ব্যবহার করে বলেন ক্যাচিং অস্থায়ী টেবিলগুলি ব্যবহার করার কিছু সুবিধা রয়েছে।
পদ্ধতি
সাধারণত
আমি "শিরোনাম" ক্যোয়ারী (যেমন ProductID
গুলি পেতে , এবং একটি ক্ষেত্রে এছাড়াও নির্দিষ্ট বর্ণগুলি দিয়ে শুরু করার DaysToManufacture
উপর ভিত্তি করে Name
) "বিশদ" কোয়েরিগুলি (অর্থাত্ TransactionID
এস এবং TransactionDate
এস প্রাপ্তি) থেকে আলাদা করেছি । ধারণাটি ছিল খুব সাধারণ প্রশ্নগুলি সম্পাদন করা এবং অপ্টিমাইজারটিকে তাদের সাথে যোগ দেওয়ার সময় বিভ্রান্ত হওয়ার অনুমতি না দেওয়া। স্পষ্টতই এটি সর্বদা সুবিধাজনক নয় কারণ এটি অপটিমাইজারটিকে ভাল, অনুকূলকরণ থেকেও অস্বীকার করে। কিন্তু যেমন আমরা ফলাফলগুলিতে দেখেছি, ক্যোয়ারির ধরণের উপর নির্ভর করে এই পদ্ধতিটির গুণাগুণ রয়েছে।
এই পদ্ধতির বিভিন্ন স্বাদের মধ্যে পার্থক্য হ'ল:
ধ্রুবক: পরামিতিগুলির পরিবর্তে যে কোনও প্রতিস্থাপনযোগ্য মান ইনলাইন স্থির হিসাবে জমা দিন। এটি ProductID
তিনটি পরীক্ষায় এবং টেস্ট 2-এ ফিরে আসা সারিগুলির সংখ্যা DaysToManufacture
উল্লেখ করবে কারণ এটি "পণ্যের গুণাবলীর পাঁচগুণ" function এই উপ-পদ্ধতিটির অর্থ হ'ল প্রত্যেকে ProductID
তার নিজস্ব এক্সিকিউশন প্ল্যান পাবেন, যা উপাত্ত হতে পারে যদি এর জন্য ডেটা বিতরণে বিস্তৃত ভিন্নতা থাকে ProductID
। তবে যদি ডেটা বিতরণে সামান্য তাত্পর্য হয় তবে অতিরিক্ত পরিকল্পনা উত্পন্ন করার ব্যয় সম্ভবত এটির পক্ষে উপযুক্ত হবে না।
প্যারামিটারাইজড: মৃত্যুদন্ড কার্যকর করার পরিকল্পনার ক্যাচিং এবং পুনরায় ব্যবহারের অনুমতি দিয়ে কমপক্ষে ProductID
হিসাবে জমা দিন @ProductID
। টেস্ট 2-এ প্যারামিটার হিসাবে ফিরে আসার জন্য সারিগুলির পরিবর্তনশীল সংখ্যার সাথে চিকিত্সা করার জন্য একটি অতিরিক্ত পরীক্ষার বিকল্প রয়েছে।
অপরিবর্তিত অজানা:ProductID
হিসাবে উল্লেখ করার সময় @ProductID
, যদি ডেটা বিতরণে বিস্তৃত ভিন্নতা থাকে তবে এমন একটি পরিকল্পনাকে ক্যাশে করা সম্ভব যা অন্যান্য ProductID
মানগুলিতে নেতিবাচক প্রভাব ফেলে তাই এই কোয়েরি ইঙ্গিতটি কোনও ব্যবহার করে কিনা তা জানা ভাল।
ক্যাশে পণ্যগুলি:Production.Product
প্রতিবার টেবিলটি জিজ্ঞাসা করার পরিবর্তে , ঠিক একই তালিকা পেতে একবারে ক্যোয়ারি চালান (এবং এটি উপস্থিত থাকাকালীন, টেবিলের ProductID
মধ্যে নেই এমন কোনও ফিল্টার আউট করুন TransactionHistory
যাতে আমরা কোনও অপচয় না করি don't সেখানে সংস্থানসমূহ) এবং ক্যাশে সেই তালিকা তালিকার DaysToManufacture
ক্ষেত্রটি অন্তর্ভুক্ত করা উচিত । এই অপশনটি ব্যবহার করে প্রথম সম্পাদনের জন্য লজিকাল রিডসে কিছুটা উচ্চতর প্রাথমিক আঘাত রয়েছে তবে এর পরে এটি কেবল TransactionHistory
সারণী যা অনুসন্ধান করা হয়েছে।
বিশেষভাবে
ঠিক আছে, তবে তাই, উম, কীভাবে সমস্ত সাব-কোয়েরিগুলিকে আলাদা আলাদা প্রশ্ন হিসাবে কোনও সূরসর ব্যবহার না করে এবং প্রতিটি ফলাফলকে অস্থায়ী টেবিল বা টেবিলের ভেরিয়েবলের জন্য সেট না করে পৃথক অনুসন্ধান হিসাবে ইস্যু করা সম্ভব? পরিষ্কারভাবে কর্সার / টেম্প টেবিল পদ্ধতিটি করা স্পষ্টভাবে প্রতিচ্ছবি পড়বে রিডস এবং রাইটসে। ওয়েল, এসকিউএলসিআরআর :) ব্যবহার করে। একটি এসকিউএলসিআরআর সঞ্চিত পদ্ধতি তৈরি করে আমি একটি ফলাফল সেট খুলতে সক্ষম হয়েছি এবং অবিচ্ছিন্ন ফলাফল সেট হিসাবে (এবং একাধিক ফলাফল সেট নয়) হিসাবে প্রতিটি সাব-কোয়েরির ফলাফলগুলি মূলত এটিতে প্রবাহিত করতে সক্ষম হয়েছি। পণ্য সম্পর্কিত তথ্য বাইরে (অর্থাত ProductID
, Name
এবংDaysToManufacture
), সাব-কোয়েরির ফলাফলগুলির কোনও একটিও কোথাও (মেমরি বা ডিস্ক) সংরক্ষণ করা হয়নি এবং এসকিউএলসিআরআর স্টোরেজ পদ্ধতির মূল ফলাফল সেট হিসাবে পেরিয়ে গেছে। এটি আমাকে পণ্যের তথ্য পেতে একটি সাধারণ ক্যোয়ারী করার অনুমতি দেয় এবং তারপরে চক্রটি চালিয়ে, এর বিরুদ্ধে খুব সাধারণ প্রশ্নগুলি জারি করে TransactionHistory
।
এবং, এজন্য আমাকে পরিসংখ্যানগুলি ক্যাপচার করতে এসকিউএল সার্ভার প্রোফাইলার ব্যবহার করতে হয়েছিল। এসকিউএলসিআরআর সঞ্চিত পদ্ধতিটি "প্রকৃত কার্যনির্বাহী পরিকল্পনা অন্তর্ভুক্ত করুন" অনুসন্ধান বিকল্পটি সেট করে অথবা জারি করে কোনও কার্যকরকরণ পরিকল্পনা ফেরত দেয় না SET STATISTICS XML ON;
।
পণ্য তথ্য ক্যাশে করার জন্য, আমি একটি readonly static
জেনেরিক তালিকা ব্যবহার করেছি (উদাহরণস্বরূপ _GlobalProducts
নীচের কোডে)। মনে হচ্ছে যে সংগ্রহগুলি যোগ লঙ্ঘন করে না readonly
বিকল্প, অত: পর এই কোড কাজ করে সমাবেশ টি PERMISSON_SET
এর SAFE
:), যে এমনকি যদি পাল্টা স্বজ্ঞাত।
উত্পন্ন উত্সগুলি
এই এসকিউএলসিআরআর সঞ্চিত পদ্ধতি দ্বারা উত্পন্ন প্রশ্নগুলি নীচে রয়েছে:
পণ্যর বিবরণ
পরীক্ষার নম্বর 1 এবং 3 (কোনও ক্যাচিং নেই)
SELECT prod1.ProductID, prod1.Name, 1 AS [DaysToManufacture]
FROM Production.Product prod1
WHERE prod1.Name LIKE N'[M-R]%';
পরীক্ষার নম্বর 2 (কোনও ক্যাচিং নেই)
;WITH cte AS
(
SELECT prod1.ProductID
FROM Production.Product prod1 WITH (INDEX(AK_Product_Name))
WHERE prod1.Name LIKE N'[M-R]%'
)
SELECT prod2.ProductID, prod2.Name, prod2.DaysToManufacture
FROM Production.Product prod2
INNER JOIN cte
ON cte.ProductID = prod2.ProductID;
পরীক্ষার নম্বর 1, 2 এবং 3 (ক্যাচিং)
;WITH cte AS
(
SELECT prod1.ProductID
FROM Production.Product prod1 WITH (INDEX(AK_Product_Name))
WHERE prod1.Name LIKE N'[M-R]%'
AND EXISTS (
SELECT *
FROM Production.TransactionHistory th
WHERE th.ProductID = prod1.ProductID
)
)
SELECT prod2.ProductID, prod2.Name, prod2.DaysToManufacture
FROM Production.Product prod2
INNER JOIN cte
ON cte.ProductID = prod2.ProductID;
লেনদেনের তথ্য
পরীক্ষার নম্বর 1 এবং 2 (ধ্রুবক)
SELECT TOP (5) th.TransactionID, th.TransactionDate
FROM Production.TransactionHistory th
WHERE th.ProductID = 977
ORDER BY th.TransactionDate DESC;
পরীক্ষার নম্বর 1 এবং 2 (প্যারামিটারাইজড)
SELECT TOP (5) th.TransactionID, th.TransactionDate
FROM Production.TransactionHistory th
WHERE th.ProductID = @ProductID
ORDER BY th.TransactionDate DESC
;
পরীক্ষার নম্বর 1 এবং 2 (প্যারামিটারাইজড + অপ্টিমাইজ অজানা)
SELECT TOP (5) th.TransactionID, th.TransactionDate
FROM Production.TransactionHistory th
WHERE th.ProductID = @ProductID
ORDER BY th.TransactionDate DESC
OPTION (OPTIMIZE FOR (@ProductID UNKNOWN));
পরীক্ষার নম্বর 2 (উভয়ই প্যারামিটারাইজড)
SELECT TOP (@RowsToReturn) th.TransactionID, th.TransactionDate
FROM Production.TransactionHistory th
WHERE th.ProductID = @ProductID
ORDER BY th.TransactionDate DESC
;
পরীক্ষার নম্বর 2 (উভয়কেই প্যারামিটারাইজড + অপটিমাইজ করুন অজানা)
SELECT TOP (@RowsToReturn) th.TransactionID, th.TransactionDate
FROM Production.TransactionHistory th
WHERE th.ProductID = @ProductID
ORDER BY th.TransactionDate DESC
OPTION (OPTIMIZE FOR (@ProductID UNKNOWN));
পরীক্ষার নম্বর 3 (ধ্রুবক)
SELECT TOP (1) th.TransactionID, th.TransactionDate
FROM Production.TransactionHistory th
WHERE th.ProductID = 977
ORDER BY th.TransactionDate DESC, th.TransactionID DESC;
পরীক্ষার নম্বর 3 (প্যারামিটারাইজড)
SELECT TOP (1) th.TransactionID, th.TransactionDate
FROM Production.TransactionHistory th
WHERE th.ProductID = @ProductID
ORDER BY th.TransactionDate DESC, th.TransactionID DESC
;
পরীক্ষার নম্বর 3 (প্যারামিটারাইজড + অপটিমাইজ অচলিত)
SELECT TOP (1) th.TransactionID, th.TransactionDate
FROM Production.TransactionHistory th
WHERE th.ProductID = @ProductID
ORDER BY th.TransactionDate DESC, th.TransactionID DESC
OPTION (OPTIMIZE FOR (@ProductID UNKNOWN));
কোড
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public class ObligatoryClassName
{
private class ProductInfo
{
public int ProductID;
public string Name;
public int DaysToManufacture;
public ProductInfo(int ProductID, string Name, int DaysToManufacture)
{
this.ProductID = ProductID;
this.Name = Name;
this.DaysToManufacture = DaysToManufacture;
return;
}
}
private static readonly List<ProductInfo> _GlobalProducts = new List<ProductInfo>();
private static void PopulateGlobalProducts(SqlBoolean PrintQuery)
{
if (_GlobalProducts.Count > 0)
{
if (PrintQuery.IsTrue)
{
SqlContext.Pipe.Send(String.Concat("I already haz ", _GlobalProducts.Count,
" entries :)"));
}
return;
}
SqlConnection _Connection = new SqlConnection("Context Connection = true;");
SqlCommand _Command = new SqlCommand();
_Command.CommandType = CommandType.Text;
_Command.Connection = _Connection;
_Command.CommandText = @"
;WITH cte AS
(
SELECT prod1.ProductID
FROM Production.Product prod1 WITH (INDEX(AK_Product_Name))
WHERE prod1.Name LIKE N'[M-R]%'
AND EXISTS (
SELECT *
FROM Production.TransactionHistory th
WHERE th.ProductID = prod1.ProductID
)
)
SELECT prod2.ProductID, prod2.Name, prod2.DaysToManufacture
FROM Production.Product prod2
INNER JOIN cte
ON cte.ProductID = prod2.ProductID;
";
SqlDataReader _Reader = null;
try
{
_Connection.Open();
_Reader = _Command.ExecuteReader();
while (_Reader.Read())
{
_GlobalProducts.Add(new ProductInfo(_Reader.GetInt32(0), _Reader.GetString(1),
_Reader.GetInt32(2)));
}
}
catch
{
throw;
}
finally
{
if (_Reader != null && !_Reader.IsClosed)
{
_Reader.Close();
}
if (_Connection != null && _Connection.State != ConnectionState.Closed)
{
_Connection.Close();
}
if (PrintQuery.IsTrue)
{
SqlContext.Pipe.Send(_Command.CommandText);
}
}
return;
}
[Microsoft.SqlServer.Server.SqlProcedure]
public static void GetTopRowsPerGroup(SqlByte TestNumber,
SqlByte ParameterizeProductID, SqlBoolean OptimizeForUnknown,
SqlBoolean UseSequentialAccess, SqlBoolean CacheProducts, SqlBoolean PrintQueries)
{
SqlConnection _Connection = new SqlConnection("Context Connection = true;");
SqlCommand _Command = new SqlCommand();
_Command.CommandType = CommandType.Text;
_Command.Connection = _Connection;
List<ProductInfo> _Products = null;
SqlDataReader _Reader = null;
int _RowsToGet = 5; // default value is for Test Number 1
string _OrderByTransactionID = "";
string _OptimizeForUnknown = "";
CommandBehavior _CmdBehavior = CommandBehavior.Default;
if (OptimizeForUnknown.IsTrue)
{
_OptimizeForUnknown = "OPTION (OPTIMIZE FOR (@ProductID UNKNOWN))";
}
if (UseSequentialAccess.IsTrue)
{
_CmdBehavior = CommandBehavior.SequentialAccess;
}
if (CacheProducts.IsTrue)
{
PopulateGlobalProducts(PrintQueries);
}
else
{
_Products = new List<ProductInfo>();
}
if (TestNumber.Value == 2)
{
_Command.CommandText = @"
;WITH cte AS
(
SELECT prod1.ProductID
FROM Production.Product prod1 WITH (INDEX(AK_Product_Name))
WHERE prod1.Name LIKE N'[M-R]%'
)
SELECT prod2.ProductID, prod2.Name, prod2.DaysToManufacture
FROM Production.Product prod2
INNER JOIN cte
ON cte.ProductID = prod2.ProductID;
";
}
else
{
_Command.CommandText = @"
SELECT prod1.ProductID, prod1.Name, 1 AS [DaysToManufacture]
FROM Production.Product prod1
WHERE prod1.Name LIKE N'[M-R]%';
";
if (TestNumber.Value == 3)
{
_RowsToGet = 1;
_OrderByTransactionID = ", th.TransactionID DESC";
}
}
try
{
_Connection.Open();
// Populate Product list for this run if not using the Product Cache
if (!CacheProducts.IsTrue)
{
_Reader = _Command.ExecuteReader(_CmdBehavior);
while (_Reader.Read())
{
_Products.Add(new ProductInfo(_Reader.GetInt32(0), _Reader.GetString(1),
_Reader.GetInt32(2)));
}
_Reader.Close();
if (PrintQueries.IsTrue)
{
SqlContext.Pipe.Send(_Command.CommandText);
}
}
else
{
_Products = _GlobalProducts;
}
SqlDataRecord _ResultRow = new SqlDataRecord(
new SqlMetaData[]{
new SqlMetaData("ProductID", SqlDbType.Int),
new SqlMetaData("Name", SqlDbType.NVarChar, 50),
new SqlMetaData("TransactionID", SqlDbType.Int),
new SqlMetaData("TransactionDate", SqlDbType.DateTime)
});
SqlParameter _ProductID = new SqlParameter("@ProductID", SqlDbType.Int);
_Command.Parameters.Add(_ProductID);
SqlParameter _RowsToReturn = new SqlParameter("@RowsToReturn", SqlDbType.Int);
_Command.Parameters.Add(_RowsToReturn);
SqlContext.Pipe.SendResultsStart(_ResultRow);
for (int _Row = 0; _Row < _Products.Count; _Row++)
{
// Tests 1 and 3 use previously set static values for _RowsToGet
if (TestNumber.Value == 2)
{
if (_Products[_Row].DaysToManufacture == 0)
{
continue; // no use in issuing SELECT TOP (0) query
}
_RowsToGet = (5 * _Products[_Row].DaysToManufacture);
}
_ResultRow.SetInt32(0, _Products[_Row].ProductID);
_ResultRow.SetString(1, _Products[_Row].Name);
switch (ParameterizeProductID.Value)
{
case 0x01:
_Command.CommandText = String.Format(@"
SELECT TOP ({0}) th.TransactionID, th.TransactionDate
FROM Production.TransactionHistory th
WHERE th.ProductID = @ProductID
ORDER BY th.TransactionDate DESC{2}
{1};
", _RowsToGet, _OptimizeForUnknown, _OrderByTransactionID);
_ProductID.Value = _Products[_Row].ProductID;
break;
case 0x02:
_Command.CommandText = String.Format(@"
SELECT TOP (@RowsToReturn) th.TransactionID, th.TransactionDate
FROM Production.TransactionHistory th
WHERE th.ProductID = @ProductID
ORDER BY th.TransactionDate DESC
{0};
", _OptimizeForUnknown);
_ProductID.Value = _Products[_Row].ProductID;
_RowsToReturn.Value = _RowsToGet;
break;
default:
_Command.CommandText = String.Format(@"
SELECT TOP ({0}) th.TransactionID, th.TransactionDate
FROM Production.TransactionHistory th
WHERE th.ProductID = {1}
ORDER BY th.TransactionDate DESC{2};
", _RowsToGet, _Products[_Row].ProductID, _OrderByTransactionID);
break;
}
_Reader = _Command.ExecuteReader(_CmdBehavior);
while (_Reader.Read())
{
_ResultRow.SetInt32(2, _Reader.GetInt32(0));
_ResultRow.SetDateTime(3, _Reader.GetDateTime(1));
SqlContext.Pipe.SendResultsRow(_ResultRow);
}
_Reader.Close();
}
}
catch
{
throw;
}
finally
{
if (SqlContext.Pipe.IsSendingResults)
{
SqlContext.Pipe.SendResultsEnd();
}
if (_Reader != null && !_Reader.IsClosed)
{
_Reader.Close();
}
if (_Connection != null && _Connection.State != ConnectionState.Closed)
{
_Connection.Close();
}
if (PrintQueries.IsTrue)
{
SqlContext.Pipe.Send(_Command.CommandText);
}
}
}
}
টেস্ট প্রশ্ন
এখানে পরীক্ষাগুলি পোস্ট করার মতো পর্যাপ্ত জায়গা নেই তাই আমি অন্য একটি জায়গা খুঁজে পাব।
উপসংহার
নির্দিষ্ট পরিস্থিতিতে, এসকিউএলসিআরআর টি-এসকিউএল-তে করা যায় না এমন প্রশ্নের কয়েকটি দিক ম্যানিপুলেট করতে ব্যবহার করা যেতে পারে। টেম্প টেবিলের পরিবর্তে ক্যাশিংয়ের জন্য মেমরি ব্যবহার করার ক্ষমতা রয়েছে, যদিও স্মৃতি মেমরিটি স্বয়ংক্রিয়ভাবে সিস্টেমে ফিরে আসে না বলে এটি খুব কম এবং সাবধানতার সাথে করা উচিত। এই পদ্ধতিটি এমন কোনও বিষয় নয় যা অ্যাডহক কোয়েরিগুলিকে সহায়তা করবে, যদিও এখানে সম্পাদন করা প্রশ্নের আরও বেশি দিকগুলি উপস্থাপনের জন্য প্যারামিটারগুলি যুক্ত করে আমি কেবল এখানে দেখানোর চেয়ে আরও নমনীয় করে তোলা সম্ভব।
হালনাগাদ
অতিরিক্ত পরীক্ষা
আমার মূল পরীক্ষাগুলিতে TransactionHistory
নিম্নলিখিত সংজ্ঞাটিতে একটি সহায়ক সূচক অন্তর্ভুক্ত ছিল :
ProductID ASC, TransactionDate DESC
আমি শেষ পর্যন্ত অন্তর্ভুক্ত করার সিদ্ধান্ত নিয়েছিলাম, ভেবেছিলাম যে TransactionId DESC
এটি টেস্ট 3 TransactionId
নম্বরকে সহায়তা করতে পারে (যা সবচেয়ে সাম্প্রতিক সময়ে ওয়েল ব্রেকিং নির্দিষ্ট করে - তবে "অতি সাম্প্রতিক" অনুমান করা হয়েছে যেহেতু স্পষ্টভাবে বলা হয়নি, তবে সবাই মনে হয় এই অনুমানের সাথে একমত হওয়ার জন্য), সম্ভবত কোনও পার্থক্য করার পক্ষে পর্যাপ্ত সম্পর্ক থাকবে না।
কিন্তু, তারপরে অ্যারন একটি সমর্থনকারী সূচক দিয়ে প্রতিক্রিয়া ব্যক্ত করেছিলেন যা এতে অন্তর্ভুক্ত ছিল TransactionId DESC
এবং দেখা গেছে যে CROSS APPLY
পদ্ধতিটি তিনটি পরীক্ষার মধ্যেই বিজয়ী। এটি আমার পরীক্ষার চেয়ে পৃথক ছিল যা ইঙ্গিত দেয় যে সিটিই পদ্ধতিটি টেস্ট নম্বর 3 এর জন্য সবচেয়ে ভাল ছিল (যখন কোনও ক্যাচিং ব্যবহার করা হয়নি, যা হারুনের পরীক্ষাকে মিরর করে)। এটি স্পষ্ট ছিল যে একটি অতিরিক্ত প্রকরণ ছিল যা পরীক্ষা করার প্রয়োজন ছিল।
আমি বর্তমান সমর্থনকারী সূচকটি সরিয়েছি, এর সাথে একটি নতুন তৈরি করেছি TransactionId
এবং পরিকল্পনার ক্যাশে সাফ করেছি (কেবল নিশ্চিত হওয়ার জন্য):
DROP INDEX [IX_TransactionHistoryX] ON Production.TransactionHistory;
CREATE UNIQUE INDEX [UIX_TransactionHistoryX]
ON Production.TransactionHistory (ProductID ASC, TransactionDate DESC, TransactionID DESC)
WITH (FILLFACTOR = 100);
DBCC FREEPROCCACHE WITH NO_INFOMSGS;
আমি টেস্ট নম্বর 1 পুনরায় দৌড়েছি এবং ফলাফলগুলি প্রত্যাশার মতোই ছিল। আমি তখন টেস্ট নম্বর 3 পুনরায় দৌড়েছি এবং ফলাফলগুলি সত্যিই পরিবর্তিত হয়েছিল:
উপরের ফলাফলগুলি স্ট্যান্ডার্ড, নন-ক্যাচিং পরীক্ষার জন্য। এবার CROSS APPLY
, কেবল সিটিইকে পরাজিত করে না (ঠিক যেমন অ্যারোন টেস্ট নির্দেশিত), তবে এসকিউএলসিআরআর প্রোগ 30 রিডস দ্বারা নেতৃত্ব নিয়েছে (উও হু)।
উপরের ফলাফলগুলি ক্যাশিং সক্ষম করার সাথে পরীক্ষার জন্য। এবার সিটিইর পারফরম্যান্স হ্রাস পায় না, তবুও এটি CROSS APPLY
মারধর করে। যাইহোক, এখন এসকিউএলসিআরআর প্রোগ 23 রিডের নেতৃত্বে নেয় (আবারও হু, হু)।
অ্যাভয়েস নিন
ব্যবহার করার বিভিন্ন বিকল্প রয়েছে। তাদের প্রত্যেকের শক্তি রয়েছে বলে কয়েকটি চেষ্টা করা ভাল। এখানে করা পরীক্ষাগুলি সমস্ত পরীক্ষার জুড়ে সেরা এবং সবচেয়ে খারাপ অভিনেতা (একটি সমর্থনকারী সূচক সহ) এর মধ্যে পঠন এবং সময় উভয় ক্ষেত্রেই একটি ছোট পার্থক্য দেখায়; Reads এর প্রকরণটি প্রায় 350 এবং সময়কাল 55 এমএস। এসকিউএলসিআরআর প্রোক 1 টি ছাড়া সমস্ত পরীক্ষায় জিতেছে (পড়ার শর্তাবলী), কেবলমাত্র কয়েকটি পাঠ সংরক্ষণ করা এসকিউএলসিএলআর রুটে যাওয়ার রক্ষণাবেক্ষণ ব্যয়ের পক্ষে উপযুক্ত নয়। তবে অ্যাডভেঞ্চার ওয়ার্কস ২০১২-এ, টেবিলটিতে Product
কেবল 504 টি সারি রয়েছে এবং TransactionHistory
কেবল 113,443 সারি রয়েছে। এই পদ্ধতিগুলির পারফরম্যান্সের পার্থক্যটি সম্ভবত সারি সংখ্যা বাড়ার সাথে আরও প্রকট হয়ে উঠবে।
এই প্রশ্নটি সারিগুলির একটি নির্দিষ্ট সেট পাওয়ার জন্য নির্দিষ্ট ছিল, তবে এটি উপেক্ষা করা উচিত নয় যে পারফরম্যান্সের একক বৃহত্তম ফ্যাক্টরটি নির্দিষ্ট এসকিউএল নয়। কোন পদ্ধতিটি সত্যই সেরা তা নির্ধারণ করার আগে একটি ভাল সূচকের জায়গা হওয়া দরকার।
এখানে পাওয়া সবচেয়ে গুরুত্বপূর্ণ পাঠটি ক্রস অ্যাপ্লাই বনাম সিটিই বনাম এসকিউএলসিআরআর সম্পর্কিত নয়: এটি পরীক্ষা সংক্রান্ত T ধরে নেই। বেশ কয়েকটি লোকের কাছ থেকে ধারণা পান এবং যতটা সম্ভব পরিস্থিতি পরীক্ষা করুন।