অফসেট ... FETCH এবং পুরানো ধাঁচের ROW_NUMBER স্কিমের মধ্যে কেন কার্যকর করার পরিকল্পনার পার্থক্য রয়েছে?


15

নতুন OFFSET ... FETCHমডেলটি এসকিউএল সার্ভার 2012 এর সাথে পরিচয় করিয়ে দেয় সহজ এবং দ্রুত পেজিং অফার করে। দুটি ফর্ম শব্দার্থগতভাবে অভিন্ন এবং খুব সাধারণ যে বিবেচনা করে কেন কোনও পার্থক্য রয়েছে?

কেউ ধরে নিতে পারে যে অপটিমাইজার উভয়কেই স্বীকৃতি দেয় এবং তাদের (তুচ্ছভাবে) পুরোপুরিভাবে অনুকূল করে তোলে।

এখানে OFFSET ... FETCHব্যয় নির্ধারিত অনুসারে x 2x দ্রুততর একটি খুব সাধারণ কেস ।

SELECT * INTO #objects FROM sys.objects

SELECT *
FROM (
    SELECT *, ROW_NUMBER() OVER (ORDER BY object_id) r
    FROM #objects
) x
WHERE r >= 30 AND r < (30 + 10)
    ORDER BY object_id

SELECT *
FROM #objects
ORDER BY object_id
OFFSET 30 ROWS FETCH NEXT 10 ROWS ONLY

অফসেট-fetch.png

একটি সিআই তৈরি object_idবা ফিল্টার যুক্ত করে এই পরীক্ষার কেসটি পরিবর্তিত করতে পারে তবে পরিকল্পনার সমস্ত পার্থক্য অপসারণ করা অসম্ভব। OFFSET ... FETCHএটি সর্বদা দ্রুত হয় কারণ এটি কার্যকর করার সময় কম কাজ করে।


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

উত্তর:


13

প্রশ্নের উদাহরণগুলিতে একই ফলাফলগুলি বেশিরভাগই উত্পন্ন হয় না ( OFFSETউদাহরণটিতে একের পর এক ত্রুটি রয়েছে)। নীচের আপডেট হওয়া ফর্মগুলি এই সমস্যার সমাধান করে, কেসটির অতিরিক্ত ধরণের সরিয়ে ফেলুন ROW_NUMBERএবং সমাধানটিকে আরও সাধারণ করার জন্য ভেরিয়েবলগুলি ব্যবহার করুন:

DECLARE 
    @PageSize bigint = 10,
    @PageNumber integer = 3;

WITH Numbered AS
(
    SELECT TOP ((@PageNumber + 1) * @PageSize) 
        o.*,
        rn = ROW_NUMBER() OVER (
            ORDER BY o.[object_id])
    FROM #objects AS o
    ORDER BY 
        o.[object_id]
)
SELECT
    x.name,
    x.[object_id],
    x.principal_id,
    x.[schema_id],
    x.parent_object_id,
    x.[type],
    x.type_desc,
    x.create_date,
    x.modify_date,
    x.is_ms_shipped,
    x.is_published,
    x.is_schema_published
FROM Numbered AS x
WHERE
    x.rn >= @PageNumber * @PageSize
    AND x.rn < ((@PageNumber + 1) * @PageSize)
ORDER BY
    x.[object_id];

SELECT
    o.name,
    o.[object_id],
    o.principal_id,
    o.[schema_id],
    o.parent_object_id,
    o.[type],
    o.type_desc,
    o.create_date,
    o.modify_date,
    o.is_ms_shipped,
    o.is_published,
    o.is_schema_published
FROM #objects AS o
ORDER BY 
    o.[object_id]
    OFFSET @PageNumber * @PageSize - 1 ROWS 
    FETCH NEXT @PageSize ROWS ONLY;

ROW_NUMBERপরিকল্পনা আনুমানিক খরচ হয়েছে 0.0197935 :

সারি সংখ্যা পরিকল্পনা

OFFSETপরিকল্পনা আনুমানিক খরচ হয়েছে 0.0196955 :

অফসেট পরিকল্পনা

এটি 0.000098 আনুমানিক ব্যয় ইউনিটের একটি সাশ্রয় (যদিও OFFSETপরিকল্পনার জন্য অতিরিক্ত অপারেটরগুলির প্রয়োজন হবে যদি আপনি প্রতিটি সারির জন্য একটি সারি নম্বর ফিরিয়ে দিতে চান)। OFFSETপ্ল্যান এখনও সামান্য সস্তা হবে, সাধারণভাবে বলতে, কিন্তু মনে রাখবেন যে আনুমানিক খরচ ঠিক যে - রিয়েল পরীক্ষামূলক এখনও প্রয়োজন। উভয় পরিকল্পনার মূল ব্যয় হ'ল ইনপুট সেটটির সম্পূর্ণ সাজানোর ব্যয়, তাই সহায়ক সূচীগুলি উভয় সমাধানের উপকার করতে পারে।

যেখানে ধ্রুবক আক্ষরিক মান ব্যবহার করা হয় (উদাহরণস্বরূপ OFFSET 30মূল উদাহরণে) অপ্টিমাইজার শীর্ষের পরে একটি সম্পূর্ণ সাজানোর পরিবর্তে একটি শীর্ষN বাছাই করতে পারে। টপএন বাছাইয়ের জন্য সারিগুলি যখন একটি ধ্রুবক আক্ষরিক হয় এবং <= 100 (যোগফল OFFSETএবং যোগফল FETCH) হয় তখন এক্সিকিউশন ইঞ্জিন একটি আলাদা ধরণের অ্যালগরিদম ব্যবহার করতে পারে যা সাধারণ টপএন সাজানোর চেয়ে দ্রুত সম্পাদন করতে পারে। তিনটি ক্ষেত্রেই সামগ্রিকভাবে পারফরম্যান্সের বৈশিষ্ট্য রয়েছে।

কেন অপ্টিমাইজারটি ROW_NUMBERসিনট্যাক্স প্যাটার্নটি ব্যবহারের জন্য স্বয়ংক্রিয়ভাবে রূপান্তর করে না OFFSET, এর বিভিন্ন কারণ রয়েছে:

  1. এমন একটি রূপান্তর লিখতে প্রায় অসম্ভব যা সমস্ত বিদ্যমান ব্যবহারের সাথে মেলে
  2. কিছু পেজিংয়ের প্রশ্নগুলি স্বয়ংক্রিয়ভাবে পরিবর্তিত হয়েছে এবং অন্যেরা বিভ্রান্ত হতে পারে না
  3. OFFSETপরিকল্পনা সব ক্ষেত্রেই ভালো হতে নিশ্চিত করা হয় না

উপরের তৃতীয় পয়েন্টের একটি উদাহরণ দেখা যায় যেখানে পেজিং সেটটি বেশ প্রশস্ত থাকে। এটা তোলে আরো অনেক কিছু দক্ষ হতে পারে কি প্রয়োজন চাইতে একটি nonclustered সূচক এবং ম্যানুয়ালি ক্লাস্টার সূচক বিরুদ্ধে লুকআপ সঙ্গে সূচক স্ক্যান সঙ্গে তুলনা ব্যবহার OFFSETবা ROW_NUMBER। পেজিং অ্যাপ্লিকেশনটিতে মোট কতগুলি সারি বা পৃষ্ঠাগুলি রয়েছে তা জানতে হবে কিনা তা বিবেচনা করার জন্য অতিরিক্ত সমস্যা রয়েছে। সেখানে আপেক্ষিক যথার্থতা আরেকটি ভাল আলোচনা 'কী চাইতে' এবং 'অফসেট' পদ্ধতি এখানে

সামগ্রিকভাবে, ভালতর OFFSETপরীক্ষা-নিরীক্ষার পরে লোকেরা তাদের পেজিং কোয়েরিগুলি যথাযথভাবে পরীক্ষা করার পরে ব্যবহার করার জন্য পরিবর্তন করার জন্য একটি অবগত সিদ্ধান্ত গ্রহণ করবে better


1
সুতরাং সাধারণ ক্ষেত্রে রূপান্তরটি না করার কারণ সম্ভবত এটি একটি গ্রহণযোগ্য ইঞ্জিনিয়ারিং ট্রেড অফ খুঁজে পাওয়া খুব কঠিন ছিল। কেন এমনটি হতে পারে তার জন্য আপনি ভাল কারণ সরবরাহ করেছিলেন; আমি অবশ্যই বলব যে এটি একটি ভাল উত্তর। অনেক অন্তর্দৃষ্টি এবং নতুন চিন্তা। আমি প্রশ্নটি কিছুক্ষণের জন্য উন্মুক্ত রেখে দেব এবং তারপরে সেরা উত্তরটি বেছে নেব।
usr ডিরেক্টরির

5

আপনার ক্যোয়ারির সামান্য বিস্ময় নিয়ে আমি একটি সমান ব্যয়ের প্রাক্কলন (50/50) এবং সমান আইও পরিসংখ্যান পাই :

; WITH cte AS
(
    SELECT *, ROW_NUMBER() OVER (ORDER BY object_id) r
    FROM #objects
)
SELECT *
FROM cte
WHERE r >= 30 AND r < 40
ORDER BY r

SELECT *
FROM #objects
ORDER BY object_id
OFFSET 30 ROWS FETCH NEXT 10 ROWS ONLY

এটির rপরিবর্তে বাছাই করে আপনার সংস্করণে প্রদর্শিত অতিরিক্ত বাছাই এড়িয়ে যায় object_id


এই অন্তর্দৃষ্টি জন্য আপনাকে ধন্যবাদ। এখন আমি এটি সম্পর্কে যা ভাবছি তা দেখেছি অপ্টিমাইজারটি আগে ROW_NUMBER আউটপুটটির সাজানো প্রকৃতি বুঝতে পারে না। এটি সেটটিকে অবজেক্ট_আইডি দ্বারা আনর্ডারডড হিসাবে বিবেচনা করে। অথবা কমপক্ষে আর এবং অবজেক্ট_আইডি উভয় অনুসারে বাছাই করা হয়নি।
usr ডিরেক্টরির

2
ROW_NUMBER () ব্যবহার করে যে অর্ডারটি দেওয়া হয়েছে তার মধ্যে ক্লিক করুন @ এটি সংখ্যাকে কীভাবে নির্ধারণ করে তা নির্ধারণ করে। এটি আউটপুট অর্ডার প্রতিশ্রুতি দিতে কিছুই করে না - এটি পৃথক। এটি ঠিক তাই ঘটে যে এটি প্রায়শই মিলিত হয় তবে এটির নিশ্চয়তা নেই।
অ্যারন বারট্র্যান্ড

অ্যারোনবার্ট্র্যান্ড আমি বুঝতে পারি যে ROW_NUMBER আউটপুট অর্ডার করে না। তবে যদি ROW_NUMBER আউটপুট হিসাবে একই কলামগুলির দ্বারা অর্ডার করা হয়, তবে একই আদেশের নিশ্চয়তা আছে, তাই না? সুতরাং ক্যোয়ারী অপ্টিমাইজার সেই সত্যটি ব্যবহার করতে পারে। সুতরাং দুটি ক্রম ক্রিয়াকলাপ সর্বদা এই কোয়েরিতে অপরিবর্তিত থাকে।
usr ডিরেক্টরির

1
@ অাস আপনি এমন একটি সাধারণ ব্যবহারের ক্ষেত্রে হিট করেছেন যেটি অপ্টিমাইজারের জন্য অ্যাকাউন্ট করে না, তবে এটি একমাত্র ব্যবহারের ক্ষেত্রে নয়। ROW_NUMBER () এর অভ্যন্তরে অর্ডারটি সেই কলাম এবং অন্য কিছু। বা যখন বাইরের ক্রমটি অন্য কলামে গৌণ বাছাই করে। অথবা যখন আপনি নামার আদেশ করতে চান। বা সম্পূর্ণ অন্য কিছু দ্বারা। আমি rবেস কলামের পরিবর্তে এক্সপ্রেশনটি অর্ডার করতে পছন্দ করি , যদি কেবল এটি অনাবৃত ক্যোয়ারিতে আমি যা করব তার সাথে মেলে এবং কোনও অভিব্যক্তি দ্বারা অর্ডার করি - তবে আমি এক্সপ্রেশনটির পুনরাবৃত্তি না করে এক্সপ্রেশনটির জন্য বরাদ্দ করা উপনামটি ব্যবহার করব।
অ্যারন বারট্র্যান্ড

4
@ আরআর এবং পলের বক্তব্য, এমন কিছু ঘটনা ঘটবে যেখানে আপনি অপ্টিমাইজারটিতে কার্যকারিতার ফাঁক পেতে পারেন। যদি সেগুলি স্থির করা না যায় এবং আপনি কোয়েরিটি লেখার আরও ভাল উপায় জানেন তবে আরও ভাল উপায়টি ব্যবহার করুন। রোগী: "ডক্টর, আমি এক্স করলে ব্যথা হয়।" ডাক্তার: "এক্স করবেন না।" :-)
অ্যারন বারট্র্যান্ড

-3

তারা এই বৈশিষ্ট্যটি যুক্ত করতে কোয়েরি অপ্টিমাইজারটি সংশোধন করেছেন Meaning অর্থ তারা অফসেট ... আনার আদেশটি সমর্থন করার জন্য বিশেষত প্রক্রিয়াগুলি প্রয়োগ করেছিলেন implemented শীর্ষ প্রশ্নের জন্য অন্য কথায় এসকিউএল সার্ভারকে আরও অনেক কাজ করতে হবে do এইভাবে ক্যোয়ারী পরিকল্পনার পার্থক্য।

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