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


12

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

ইউডিএফ নিজে কল করতে বেশ ব্যয়বহুল তবে তত্ত্বের মধ্যে প্রশ্নগুলি তাত্ত্বিকভাবে অপ্টিমাইজারের মাধ্যমে এমনভাবে প্রয়োগ করা যেতে পারে যাতে কেবল একবার ফাংশনটি গণনা করা দরকার। আমি এই প্রশ্নের জন্য একটি বিস্তৃত সরল উদাহরণ উপহাস করেছি। নিম্নলিখিত কোয়েরিটি আমার মেশিনে চালিত হতে 6152 এমএস লাগে:

SELECT x1.ID
FROM dbo.X_100_INTEGERS x1
WHERE x1.ID >= dbo.EXPENSIVE_UDF();

ক্যোয়ারী পরিকল্পনার ফিল্টার অপারেটর প্রস্তাব দেয় যে প্রতিটি সারির জন্য একবারে ফাংশনটি মূল্যায়ন করা হয়েছিল:

ক্যোয়ারী প্ল্যান 1

ডিডিএল এবং ডেটা প্রস্তুতি:

CREATE OR ALTER FUNCTION dbo.EXPENSIVE_UDF () RETURNS INT
AS
BEGIN
    DECLARE @tbl TABLE (VAL VARCHAR(5));

    -- make the function expensive to call
    INSERT INTO @tbl
    SELECT [VALUE]
    FROM STRING_SPLIT(REPLICATE(CAST('Z ' AS VARCHAR(MAX)), 20000), ' ');

    RETURN 1;
END;

GO

DROP TABLE IF EXISTS dbo.X_100_INTEGERS;

CREATE TABLE dbo.X_100_INTEGERS (ID INT NOT NULL);

-- insert 100 integers from 1 - 100
WITH
    L0   AS(SELECT 1 AS c UNION ALL SELECT 1),
    L1   AS(SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
    L2   AS(SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
    L3   AS(SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
    L4   AS(SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
    L5   AS(SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B),
    Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n FROM L5)
INSERT INTO dbo.X_100_INTEGERS WITH (TABLOCK)
SELECT n FROM Nums WHERE n <= 100;

উপরের উদাহরণের জন্য এখানে একটি ডিবি ফিডাল লিঙ্ক রয়েছে , যদিও কোডটি সম্পাদন করতে 18 সেকেন্ড সময় লাগে।

কিছু ক্ষেত্রে আমি ফাংশনটির কোড সম্পাদনা করতে সক্ষম হতে পারি না কারণ এটি কোনও বিক্রেতার সরবরাহ করে। অন্যান্য ক্ষেত্রে আমি পরিবর্তন করতে সক্ষম হয়েছি। আমি কীভাবে কোনও স্কেলারের ইউডিএফকে কেবল একবার জিজ্ঞাসায় মূল্যায়ন করতে বাধ্য করতে পারি?

উত্তর:


17

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

যদি কোডটি সম্পাদনা করা সম্ভব হয় তবে প্রথমে চেষ্টা করার চেষ্টা করা সম্ভব হ'ল যদি সম্ভব হয় তবে ফাংশনটিকে নির্বিচারে তৈরি করা। পল হোয়াইট এখানে উল্লেখ করেছেন যে ফাংশনটি অবশ্যই SCHEMABINDINGবিকল্পের সাথে তৈরি করা উচিত এবং ফাংশন কোডটি অবশ্যই ডিটারিমেন্টিক হতে হবে।

নিম্নলিখিত পরিবর্তন করার পরে:

CREATE OR ALTER FUNCTION dbo.EXPENSIVE_UDF () RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
    DECLARE @tbl TABLE (VAL VARCHAR(5));

    -- make the function expensive to call
    INSERT INTO @tbl
    SELECT [VALUE]
    FROM STRING_SPLIT(REPLICATE(CAST('Z ' AS VARCHAR(MAX)), 20000), ' ');

    RETURN 1;
END;

প্রশ্ন থেকে ক্যোয়ারী 64৪ এমএসে কার্যকর করা হয়েছে:

SELECT x1.ID
FROM dbo.X_100_INTEGERS x1
WHERE x1.ID >= dbo.EXPENSIVE_UDF();

ক্যোয়ারী প্ল্যানটির আর ফিল্টার অপারেটর নেই:

ক্যোয়ারী প্ল্যান 1

এটি নিশ্চিত হওয়ার জন্য যে এটি কেবলমাত্র একবার কার্যকর হয়েছিল যখন আমরা এসকিউএল সার্ভার 2016 এ প্রকাশিত নতুন sys.dm_exec_function_stats DMV ব্যবহার করতে পারি :

SELECT execution_count
FROM sys.dm_exec_function_stats
WHERE object_id = OBJECT_ID('EXPENSIVE_UDF', 'FN');

ALTERফাংশনটির বিরুদ্ধে একটি ইস্যু করা execution_countসেই বস্তুর জন্য পুনরায় সেট করবে । উপরের ক্যোয়ারী 1 প্রদান করে যার অর্থ ফাংশনটি কেবল একবার কার্যকর করা হয়েছিল।

মনে রাখবেন যে কেবলমাত্র ফাংশনটি ডিটারমিনিস্টিক এর অর্থ এই নয় যে কোনও প্রশ্নের জন্য এটি একবারে মূল্যায়ন করা হবে। আসলে, কিছু প্রশ্নের জন্য যুক্ত করা SCHEMABINDINGকর্মক্ষমতা হ্রাস করতে পারে। নিম্নলিখিত কোয়েরি বিবেচনা করুন:

WITH cte (UDF_VALUE) AS
(
    SELECT DISTINCT dbo.EXPENSIVE_UDF() UDF_VALUE
)
SELECT ID
FROM dbo.X_100_INTEGERS
INNER JOIN cte ON ID >= cte.UDF_VALUE;

DISTINCTফিল্টার অপারেটর থেকে মুক্তি পেতে অতিরিক্ত অতিরিক্ত যুক্ত করা হয়েছিল। পরিকল্পনাটি আশাব্যঞ্জক বলে মনে হচ্ছে:

ক্যোয়ারী প্ল্যান 2

তার উপর ভিত্তি করে, কেউ প্রত্যাশা করবে যে ইউডিএফ একবার মূল্যায়ন হবে এবং নেস্টেড লুপ জোনে বাইরের টেবিল হিসাবে ব্যবহৃত হবে। যাইহোক, ক্যোয়ারীটি আমার মেশিনে চালাতে 6446 এমএস লাগে। sys.dm_exec_function_statsফাংশন অনুযায়ী 100 বার কার্যকর করা হয়েছিল। কীভাবে সম্ভব? " কম্পিউট স্কেলারস, এক্সপ্রেশন এবং এক্সিকিউশন প্ল্যান পারফরম্যান্স " এ, পল হোয়াইট উল্লেখ করেছেন যে কম্পিউট স্কেলার অপারেটর পিছিয়ে দেওয়া যেতে পারে:

না প্রায়শই, একটি কম্পিউট স্কেলার কেবল একটি অভিব্যক্তি সংজ্ঞা দেয়; বাস্তবায়ন পরিকল্পনার পরে কোনও ফলাফলের প্রয়োজন না হওয়া পর্যন্ত আসল গণনা পিছিয়ে যায়।

এই ক্যোয়ারির জন্য দেখে মনে হচ্ছে ইউডিএফ কলটি প্রয়োজনীয় না হওয়া পর্যন্ত পিছিয়ে গেছে, যার সময়ে এটি 100 বার মূল্যায়ন করা হয়েছিল।

মজার বিষয় হল, সিডিই উদাহরণটি যখন আমার SCHEMABINDINGপ্রশ্নের সাথে ইউডিএফ সংজ্ঞায়িত না করা হয় তখন আমার মেশিনে 71 এমএসে কার্যকর করে । কোয়েরিটি চালিত হলে ফাংশনটি একবার কার্যকর করা হয়। এখানে তার জন্য ক্যোয়ারী পরিকল্পনাটি দেওয়া হল:

ক্যোয়ারী প্ল্যান 3

এটি স্পষ্ট নয় যে কেন কম্পিউট স্কেলার পিছিয়ে নেই। এটি হতে পারে কারণ ক্রিয়াকলাপের অ-নির্ধারিততা ক্যোয়ারী অপ্টিমাইজারটি করতে পারে এমন অপারেটরদের পুনর্বিন্যাসকে সীমাবদ্ধ করে।

বিকল্প বিকল্প হল সিটিইতে একটি ছোট টেবিল যুক্ত করা এবং সেই টেবিলের একমাত্র সারিটি জিজ্ঞাসা করা। যে কোনও ছোট টেবিলটি করবে, তবে আসুন নিম্নলিখিতটি ব্যবহার করুন:

CREATE TABLE dbo.X_ONE_ROW_TABLE (ID INT NOT NULL);

INSERT INTO dbo.X_ONE_ROW_TABLE VALUES (1);

কোয়েরিটি তখন পরিণত হয়:

WITH cte (UDF_VALUE) AS
(       
    SELECT DISTINCT dbo.EXPENSIVE_UDF() UDF_VALUE
    FROM dbo.X_ONE_ROW_TABLE
)
SELECT ID
FROM dbo.X_100_INTEGERS
INNER JOIN cte ON ID >= cte.UDF_VALUE;

সংযোজনগুলির সংযোজন dbo.X_ONE_ROW_TABLEঅপ্টিমাইজারের জন্য অনিশ্চয়তা যুক্ত করে। যদি টেবিলটিতে শূন্য সারি থাকে তবে সিটিই 0 টি সারি ফিরে আসবে। যে কোনও ক্ষেত্রে, অপ্টিমাইজার গ্যারান্টি দিতে পারে না যে ইউডিএফ যদি সংজ্ঞাবাদী না হয় তবে সিটিই এক সারিতে ফিরে আসবে, তাই সম্ভবত যুক্ত হওয়ার আগে ইউডিএফকে মূল্যায়ন করা হবে বলে মনে হয়। আমি প্রত্যাশা করব যে অপটিমাইজারটি স্ক্যান করবে dbo.X_ONE_ROW_TABLE, এক সারিটির সর্বাধিক মান ফিরে আসার জন্য একটি স্ট্রিম সমষ্টি ব্যবহার করবে (যার জন্য ফাংশনটি মূল্যায়ন করা দরকার), এবং এটি একটি নেস্টেড লুপের জন্য বাইরের টেবিল হিসাবে dbo.X_100_INTEGERSমূল ক্যোয়ারিতে যোগ দিতে হবে । এই উপস্থিত হতে পারে সেখানে কি ঘটছে :

ক্যোয়ারী প্ল্যান 4

ক্যোরিটি আমার মেশিনে প্রায় 110 এমএসে কার্যকর করে এবং ইউডিএফ কেবলমাত্র একবার অনুযায়ী মূল্যায়ন করা হয় sys.dm_exec_function_stats। এটি বলা ভুল হবে যে ক্যোয়ারী অপ্টিমাইজার কেবল একবারই ইউডিএফ মূল্যায়ন করতে বাধ্য হয়। তবে, ইউডিএফ এবং কম্পিউট স্কেলারের ব্যয়ের সীমাবদ্ধতা থাকা সত্ত্বেও, এমন একটি অপ্টিমাইজার পুনরায় লেখার কল্পনা করা শক্ত যা কম দামের ক্যোয়ারী তৈরি করে।

সংক্ষেপে, নির্বাহী ফাংশনগুলির জন্য (যা অবশ্যই SCHEMABINDINGবিকল্পটি অন্তর্ভুক্ত করতে পারে ) কোয়েরিটি যথাসম্ভব সহজভাবে লেখার চেষ্টা করুন। যদি এসকিউএল সার্ভার 2016 বা পরবর্তী সংস্করণে থাকে তবে নিশ্চিত হয়ে নিন যে ফাংশনটি একবার ব্যবহার করার পরে কার্যকর হয়েছিল sys.dm_exec_function_stats। কার্যকর করার পরিকল্পনাগুলি সে ক্ষেত্রে বিভ্রান্তিকর হতে পারে।

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

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