यह व्युत्पन्न तालिका प्रदर्शन में सुधार क्यों करती है?


18

मेरे पास एक क्वेरी है जो पैरामीटर के रूप में एक json स्ट्रिंग लेता है। Json अक्षांश, देशांतर जोड़े का एक सरणी है। एक उदाहरण इनपुट निम्नलिखित हो सकता है।

declare @json nvarchar(max)= N'[[40.7592024,-73.9771259],[40.7126492,-74.0120867]
,[41.8662374,-87.6908788],[37.784873,-122.4056546]]';

यह एक टीवीएफ कहता है जो 1,3,5,10 मील की दूरी पर एक भौगोलिक बिंदु के आसपास POI की संख्या की गणना करता है।

create or alter function [dbo].[fn_poi_in_dist](@geo geography)
returns table
with schemabinding as
return 
select count_1  = sum(iif(LatLong.STDistance(@geo) <= 1609.344e * 1,1,0e))
      ,count_3  = sum(iif(LatLong.STDistance(@geo) <= 1609.344e * 3,1,0e))
      ,count_5  = sum(iif(LatLong.STDistance(@geo) <= 1609.344e * 5,1,0e))
      ,count_10 = count(*)
from dbo.point_of_interest
where LatLong.STDistance(@geo) <= 1609.344e * 10

Json क्वेरी का आशय इस फ़ंक्शन को बल्क कॉल करना है। अगर मैं इसे इस तरह कहता हूं तो प्रदर्शन केवल 4 सेकंड के लिए लगभग 10 सेकंड ले रहा है:

select row=[key]
      ,count_1
      ,count_3
      ,count_5
      ,count_10
from openjson(@json)
cross apply dbo.fn_poi_in_dist(
            geography::Point(
                convert(float,json_value(value,'$[0]'))
               ,convert(float,json_value(value,'$[1]'))
               ,4326))

योजना = https://www.brentozar.com/pastetheplan/?id=HJDCYd_o4

हालांकि, एक व्युत्पन्न तालिका के अंदर भूगोल के निर्माण को स्थानांतरित करने से प्रदर्शन में सुधार होता है, जिससे क्वेरी लगभग 1 सेकंड में पूरी होती है।

select row=[key]
      ,count_1
      ,count_3
      ,count_5
      ,count_10
from (
select [key]
      ,geo = geography::Point(
                convert(float,json_value(value,'$[0]'))
               ,convert(float,json_value(value,'$[1]'))
               ,4326)
from openjson(@json)
) a
cross apply dbo.fn_poi_in_dist(geo)

योजना = https://www.brentozar.com/pastetheplan/?id=HkSS5_OoE

योजनाएं लगभग समान दिखती हैं। न तो समानता का उपयोग करता है और दोनों स्थानिक सूचकांक का उपयोग करते हैं। धीमी योजना पर एक अतिरिक्त आलसी स्पूल है जिसे मैं संकेत के साथ समाप्त कर सकता हूं option(no_performance_spool)। लेकिन क्वेरी प्रदर्शन नहीं बदलता है। यह अभी भी बहुत धीमा है।

एक बैच में जोड़े गए संकेत के साथ दोनों को चलाने से दोनों प्रश्नों का समान रूप से वजन होगा।

Sql सर्वर संस्करण = Microsoft SQL Server 2016 (SP1-CU7-GDR) (KB4057119) - 13.0.4466.4 (X64)

तो मेरा सवाल यह है कि यह मामला क्यों है? मुझे कैसे पता चलेगा कि मुझे किसी व्युत्पन्न तालिका के अंदर मूल्यों की गणना करनी चाहिए या नहीं?


1
"वजन" से क्या आपका मतलब अनुमानित लागत% है? यही कारण है कि संख्या लगभग व्यर्थ है, खासकर जब आप UDFs ला रहे है, JSON, भूगोल, आदि के माध्यम से CLR
हारून बर्ट्रेंड

मुझे पता है, लेकिन IO आँकड़े देख वे भी समान हैं। दोनों point_of_interestटेबल पर 358306 लॉजिकल पढ़ते हैं , दोनों इंडेक्स 4602 बार स्कैन करते हैं, और दोनों एक काम करने योग्य और वर्कफ़ाइल उत्पन्न करते हैं। आकलनकर्ता का मानना ​​है कि ये योजनाएं समान हैं, फिर भी प्रदर्शन अन्यथा कहते हैं।
माइकल बी

ऐसा लगता है कि वास्तविक सीपीयू यहां मुद्दा है, संभवतः मार्टिन ने आई / ओ की ओर इशारा किया है। दुर्भाग्य से अनुमानित लागत सीपीयू और आई / ओ पर आधारित है और हमेशा वास्तविकता में क्या होता है यह प्रतिबिंबित नहीं करता है। यदि आप सेंट्रीऑन प्लान एक्सप्लोरर ( मैं वहां काम करता हूं, लेकिन टूल बिना किसी तार के मुक्त है ) का उपयोग करके वास्तविक योजनाएं उत्पन्न करता है , तो वास्तविक लागतों को केवल सीपीयू में बदल दें, आपको बेहतर संकेतक मिल सकते हैं जहां सीपीयू समय बिताया गया था।
हारून बर्ट्रेंड

1
@MartinSmith प्रति ऑपरेटर अभी तक नहीं, नहीं। हम बयान के स्तर पर उन पर काम करते हैं। वर्तमान में हम अभी भी DMV से प्रारंभिक कार्यान्वयन पर भरोसा करते हैं, इससे पहले कि उन अतिरिक्त मैट्रिक्स को निचले स्तर पर जोड़ा गया था। और हम कुछ और काम में व्यस्त हो गए हैं जिसे आप जल्द ही देखेंगे। :-)
हारून बर्ट्रेंड

1
PS आप सीधी रेखा की दूरी की गणना करने से पहले एक साधारण अंकगणित का बॉक्स लगाकर और भी बेहतर प्रदर्शन प्राप्त कर सकते हैं। यही है, उन लोगों के लिए पहले फ़िल्टर करें जहां |LatLong.Lat - @geo.Lat| + |LatLong.Long - @geo.Long| < nआप अधिक जटिल करने से पहले मूल्य रखते हैं sqrt((LatLong.Lat - @geo.Lat)^2 + (LatLong.Long - @geo.Long)^2)। और इससे भी बेहतर, पहले ऊपरी और निचले सीमा की गणना करें, फिर LatLong.Lat > @geoLatLowerBound && LatLong.Lat < @geoLatUpperBound && LatLong.Long > @geoLongLowerBound && LatLong.Long < @geoLongUpperBound। (यह स्यूडोकोड है, उचित रूप से अनुकूलित करें।)
एरिक

जवाबों:


15

मैं आपको एक आंशिक उत्तर दे सकता हूं जो बताता है कि आप प्रदर्शन अंतर क्यों देख रहे हैं - हालांकि वह अभी भी कुछ खुले प्रश्न छोड़ता है (जैसे कि SQL सर्वर एक मध्यवर्ती तालिका अभिव्यक्ति को प्रस्तुत किए बिना अधिक इष्टतम योजना का उत्पादन कर सकता है जो अभिव्यक्ति को स्तंभ के रूप में प्रोजेक्ट करता है?)


अंतर यह है कि फास्ट प्लान में JSON सरणी तत्वों को पार्स करने और भूगोल बनाने के लिए आवश्यक कार्य 4 बार किया जाता है (एक बार openjsonफ़ंक्शन से निकलने वाली प्रत्येक पंक्ति के लिए ) - जबकि यह धीमी योजना में 100,000 से अधिक बार किया जाता है ।

तेज योजना में ...

geography::Point(
                convert(float,json_value(value,'$[0]'))
               ,convert(float,json_value(value,'$[1]'))
               ,4326)

फ़ंक्शन Expr1000के बाईं ओर कंप्यूट स्केलर में असाइन किया गया openjsonहै। यह geoआपकी व्युत्पन्न तालिका परिभाषा से मेल खाती है ।

यहाँ छवि विवरण दर्ज करें

तेजी से योजना में फिल्टर और स्ट्रीम कुल संदर्भ Expr1000। धीमी योजना में वे पूर्ण अंतर्निहित अभिव्यक्ति का संदर्भ देते हैं।

स्ट्रीम कुल गुण

यहाँ छवि विवरण दर्ज करें

प्रत्येक निष्पादन के साथ फिल्टर को 116,995 बार निष्पादित किया जाता है, जिसमें अभिव्यक्ति मूल्यांकन की आवश्यकता होती है। स्ट्रीम एग्रीगेट में 110,520 पंक्तियाँ हैं जो एकत्रीकरण के लिए इसमें बहती हैं और इस अभिव्यक्ति का उपयोग करके तीन अलग-अलग समुच्चय बनाती हैं। 110,520 * 3 + 116,995 = 448,555। यहां तक ​​कि अगर प्रत्येक व्यक्तिगत मूल्यांकन में 18 माइक्रोसेकंड लगते हैं, तो यह समग्र रूप से क्वेरी के लिए 8 सेकंड अतिरिक्त समय जोड़ता है।

आप वास्तविक समय के आँकड़ों में इसका प्रभाव एक्सएमएल योजना में देख सकते हैं (धीमी योजना से नीचे लाल रंग में और तेजी से योजना के लिए नीला - बार एमएस में हैं)

यहाँ छवि विवरण दर्ज करें

स्ट्रीम एग्रीगेट के पास अपने तत्काल बच्चे की तुलना में 6.209 सेकंड अधिक समय है। और बच्चे के समय का बड़ा हिस्सा फिल्टर द्वारा लिया गया था। यह अतिरिक्त अभिव्यक्ति मूल्यांकन से मेल खाती है।


वैसे .... सामान्य तौर पर यह कोई निश्चित बात नहीं है कि लेबल जैसे अंतर्निहित अभिव्यक्तियों Expr1000को केवल एक बार गणना की जाती है और पुनर्मूल्यांकन नहीं किया जाता है, लेकिन इस मामले में स्पष्ट रूप से निष्पादन समय की विसंगति से यह यहां होता है।


एक तरफ के रूप में, अगर मैं भूगोल उत्पन्न करने के लिए एक क्रॉस अप्लाई का उपयोग करने के लिए क्वेरी को स्विच करता हूं, तो मुझे फास्ट प्लान भी मिलता है। cross apply(select geo=geography::Point( convert(float,json_value(value,'$[0]')) ,convert(float,json_value(value,'$[1]')) ,4326))f
माइकल बी

दुर्भाग्यपूर्ण है, लेकिन मैं सोच रहा हूं कि क्या यह तेज योजना उत्पन्न करने का एक आसान तरीका है।
माइकल बी

शौकिया सवाल के लिए क्षमा करें, लेकिन आपकी छवियों में कौन सा उपकरण दिखाया गया है?
ब्लूराजा - डैनी Pflughoeft

1
@ BlueRaja-DannyPflughoeft ये प्रबंधन स्टूडियो में दिखाए गए निष्पादन योजनाएं हैं (SSMS में उपयोग किए जाने वाले आइकन हाल के संस्करणों में अपडेट किए गए हैं यदि यह प्रश्न का कारण था)
मार्टिन स्मिथ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.