मैं यह देखने की कोशिश कर रहा हूं कि क्या क्वेरी के लिए एक निश्चित योजना का उपयोग करने के लिए SQL सर्वर को ट्रिक करने का एक तरीका है।
1. पर्यावरण
कल्पना कीजिए कि आपके पास कुछ डेटा है जो विभिन्न प्रक्रियाओं के बीच साझा किया गया है। तो, मान लीजिए कि हमारे पास कुछ प्रयोग परिणाम हैं जो बहुत अधिक स्थान लेते हैं। फिर, प्रत्येक प्रक्रिया के लिए हम जानते हैं कि हम किस वर्ष / महीने के प्रयोग परिणाम का उपयोग करना चाहते हैं।
if object_id('dbo.SharedData') is not null
drop table SharedData
create table dbo.SharedData (
experiment_year int,
experiment_month int,
rn int,
calculated_number int,
primary key (experiment_year, experiment_month, rn)
)
go
अब, हर प्रक्रिया के लिए हमारे पास तालिका में सहेजे गए पैरामीटर हैं
if object_id('dbo.Params') is not null
drop table dbo.Params
create table dbo.Params (
session_id int,
experiment_year int,
experiment_month int,
primary key (session_id)
)
go
2. परीक्षण डेटा
आइए कुछ परीक्षण डेटा जोड़ें:
insert into dbo.Params (session_id, experiment_year, experiment_month)
select 1, 2014, 3 union all
select 2, 2014, 4
go
insert into dbo.SharedData (experiment_year, experiment_month, rn, calculated_number)
select
2014, 3, row_number() over(order by v1.name), abs(Checksum(newid())) % 10
from master.dbo.spt_values as v1
cross join master.dbo.spt_values as v2
go
insert into dbo.SharedData (experiment_year, experiment_month, rn, calculated_number)
select
2014, 4, row_number() over(order by v1.name), abs(Checksum(newid())) % 10
from master.dbo.spt_values as v1
cross join master.dbo.spt_values as v2
go
3. परिणाम प्राप्त करना
अब, प्रयोग के परिणाम प्राप्त करना बहुत आसान है @experiment_year/@experiment_month
:
create or alter function dbo.f_GetSharedData(@experiment_year int, @experiment_month int)
returns table
as
return (
select
d.rn,
d.calculated_number
from dbo.SharedData as d
where
d.experiment_year = @experiment_year and
d.experiment_month = @experiment_month
)
go
योजना अच्छी और समानांतर है:
select
calculated_number,
count(*)
from dbo.f_GetSharedData(2014, 4)
group by
calculated_number
क्वेरी 0 योजना
4. समस्या
लेकिन, डेटा के उपयोग को थोड़ा अधिक सामान्य बनाने के लिए, मैं एक और कार्य करना चाहता हूं - dbo.f_GetSharedDataBySession(@session_id int)
। तो, सीधा तरीका स्केलर फ़ंक्शंस बनाना होगा, अनुवाद करना @session_id
-> @experiment_year/@experiment_month
:
create or alter function dbo.fn_GetExperimentYear(@session_id int)
returns int
as
begin
return (
select
p.experiment_year
from dbo.Params as p
where
p.session_id = @session_id
)
end
go
create or alter function dbo.fn_GetExperimentMonth(@session_id int)
returns int
as
begin
return (
select
p.experiment_month
from dbo.Params as p
where
p.session_id = @session_id
)
end
go
और अब हम अपना कार्य बना सकते हैं:
create or alter function dbo.f_GetSharedDataBySession1(@session_id int)
returns table
as
return (
select
d.rn,
d.calculated_number
from dbo.f_GetSharedData(
dbo.fn_GetExperimentYear(@session_id),
dbo.fn_GetExperimentMonth(@session_id)
) as d
)
go
क्वेरी 1 योजना
यह योजना समान है, सिवाय इसके कि, समानांतर नहीं, क्योंकि स्केलर फ़ंक्शंस प्रदर्शन डेटा एक्सेस पूरे प्लान को सीरियल बनाते हैं ।
इसलिए मैंने कई अलग-अलग तरीकों की कोशिश की है, जैसे, स्केलर फ़ंक्शंस के बजाय सबक्वेरीज़ का उपयोग करना:
create or alter function dbo.f_GetSharedDataBySession2(@session_id int)
returns table
as
return (
select
d.rn,
d.calculated_number
from dbo.f_GetSharedData(
(select p.experiment_year from dbo.Params as p where p.session_id = @session_id),
(select p.experiment_month from dbo.Params as p where p.session_id = @session_id)
) as d
)
go
प्रश्न 2 योजना
या उपयोग कर रहे हैं cross apply
create or alter function dbo.f_GetSharedDataBySession3(@session_id int)
returns table
as
return (
select
d.rn,
d.calculated_number
from dbo.Params as p
cross apply dbo.f_GetSharedData(
p.experiment_year,
p.experiment_month
) as d
where
p.session_id = @session_id
)
go
प्रश्न 3 योजना
लेकिन मैं इस क्वेरी को स्केलर फ़ंक्शंस का उपयोग करने के रूप में अच्छा होने के लिए लिखने का एक तरीका नहीं ढूंढ सकता हूं।
विचारों के जोड़े:
- मूल रूप से जो मैं चाहता हूं वह यह है कि किसी तरह SQL सर्वर को कुछ मानों की पूर्व-गणना करने और फिर उन्हें स्थिरांक के रूप में आगे पारित करने में सक्षम होना चाहिए।
- यदि कुछ मध्यवर्ती भौतिकीकरण संकेत हो तो क्या सहायक हो सकता है । मैंने कुछ वेरिएंट (मल्टी-स्टेटमेंट TVF या टॉप के साथ cte) की जाँच की है, लेकिन कोई भी योजना उतनी अच्छी नहीं है, जो अब तक के स्केलर फंक्शंस के साथ है
- मैं SQL Server 2017 के आने वाले सुधार के बारे में जानता हूं - Froid: एक रिलेशनल डेटाबेस में इंपीरियल प्रोग्राम्स का अनुकूलन। मुझे यकीन नहीं है कि यह मदद करेगा, हालांकि। हालाँकि यहाँ गलत साबित होना अच्छा होता।
अतिरिक्त जानकारी
मैं एक फ़ंक्शन का उपयोग कर रहा हूं (तालिकाओं से सीधे डेटा का चयन करने के बजाय) क्योंकि यह कई अलग-अलग प्रश्नों में उपयोग करना बहुत आसान है, जो आमतौर पर @session_id
एक पैरामीटर के रूप में होता है।
मुझे वास्तविक निष्पादन समय की तुलना करने के लिए कहा गया था। इस विशेष मामले में
- क्वेरी 0 रन ~ 500ms के लिए
- क्वेरी ~ 1500ms के लिए 1 रन
- क्वेरी ~ 1500ms के लिए 2 रन
- क्वेरी ~ 2000ms के लिए 3 रन।
योजना # 2 में एक तलाश के बजाय एक सूचकांक स्कैन है, जिसे तब नेस्टेड छोरों पर विधेय द्वारा फ़िल्टर किया जाता है। प्लान # 3 उतना बुरा नहीं है, लेकिन फिर भी अधिक काम करता है और धीमी गति से काम करता है जो प्लान # 0 है।
मान लेते हैं कि dbo.Params
शायद ही कभी बदला जाता है, और आमतौर पर लगभग 1-200 पंक्तियाँ होती हैं, इससे अधिक नहीं, मान लें कि 2000 कभी अपेक्षित है। यह अब लगभग 10 कॉलम है और मुझे अक्सर कॉलम जोड़ने की उम्मीद नहीं है।
परमेस में पंक्तियों की संख्या निश्चित नहीं है, इसलिए प्रत्येक के लिए @session_id
एक पंक्ति होगी। स्तंभों की संख्या निश्चित नहीं है, यह एक कारण है कि मैं dbo.f_GetSharedData(@experiment_year int, @experiment_month int)
हर जगह से कॉल नहीं करना चाहता , इसलिए मैं आंतरिक रूप से इस क्वेरी में नया कॉलम जोड़ सकता हूं। मुझे इस पर कोई राय / सुझाव सुनने में खुशी होगी, भले ही इसमें कुछ प्रतिबंध हों।