अंकगणित संचालन के संदर्भ में परिशुद्धता और स्केल को समझना
आइए इसे नीचे तोड़ते हैं और डिवाइड अंकगणितीय ऑपरेटर के विवरण पर करीब से नज़र डालते हैं । MSDN के डिवाइड ऑपरेटर के परिणाम प्रकारों के बारे में यही कहना है :
परिणाम प्रकार
उच्च वरीयता वाले तर्क का डेटा प्रकार लौटाता है। अधिक जानकारी के लिए, डेटा प्रकार की वरीयता (Transact-SQL) देखें ।
यदि एक पूर्णांक लाभांश एक पूर्णांक भाजक द्वारा विभाजित किया जाता है, तो परिणाम एक पूर्णांक होता है जिसमें परिणाम का कोई अंश आंशिक होता है।
हम जानते हैं कि @big_numberए DECIMAL। SQL सर्वर किस डेटा प्रकार के रूप में काम करता है 1? यह एक करने के लिए यह जाती है INT। हम इसकी मदद से इसकी पुष्टि कर सकते हैं SQL_VARIANT_PROPERTY():
SELECT
SQL_VARIANT_PROPERTY(1, 'BaseType') AS [BaseType] -- int
, SQL_VARIANT_PROPERTY(1, 'Precision') AS [Precision] -- 10
, SQL_VARIANT_PROPERTY(1, 'Scale') AS [Scale] -- 0
;
किक के लिए, हम 1मूल कोड ब्लॉक को भी स्पष्ट रूप से टाइप किए गए मान के साथ बदल सकते हैं DECLARE @one INT = 1;और पुष्टि कर सकते हैं कि हमें समान परिणाम मिलते हैं।
तो हम एक DECIMALऔर एक है INT। चूंकि हमारे DECIMALपास उच्च डेटा प्रकार की पूर्ववर्ती स्थिति है INT, इसलिए हमें पता है कि हमारे डिवीजन के आउटपुट को कास्ट किया जाएगा DECIMAL।
तो समस्या कहाँ है?
समस्या DECIMALआउटपुट के पैमाने के साथ है । यहाँ पर नियमों की एक तालिका है कि SQL सर्वर अंकगणितीय परिचालनों से प्राप्त परिणामों की सटीकता और पैमाने को कैसे निर्धारित करता है :
Operation Result precision Result scale *
-------------------------------------------------------------------------------------------------
e1 + e2 max(s1, s2) + max(p1-s1, p2-s2) + 1 max(s1, s2)
e1 - e2 max(s1, s2) + max(p1-s1, p2-s2) + 1 max(s1, s2)
e1 * e2 p1 + p2 + 1 s1 + s2
e1 / e2 p1 - s1 + s2 + max(6, s1 + p2 + 1) max(6, s1 + p2 + 1)
e1 { UNION | EXCEPT | INTERSECT } e2 max(s1, s2) + max(p1-s1, p2-s2) max(s1, s2)
e1 % e2 min(p1-s1, p2 -s2) + max( s1,s2 ) max(s1, s2)
* The result precision and scale have an absolute maximum of 38. When a result
precision is greater than 38, the corresponding scale is reduced to prevent the
integral part of a result from being truncated.
और यहाँ इस तालिका में चर के लिए हमारे पास क्या है:
e1: @big_number, a DECIMAL(38, 0)
-> p1: 38
-> s1: 0
e2: 1, an INT
-> p2: 10
-> s2: 0
e1 / e2
-> Result precision: p1 - s1 + s2 + max(6, s1 + p2 + 1) = 38 + max(6, 11) = 49
-> Result scale: max(6, s1 + p2 + 1) = max(6, 11) = 11
उपरोक्त तालिका में तारांकित टिप्पणी के अनुसार, अधिकतम सटीक अधिकतम DECIMAL38 हो सकते हैं । इसलिए हमारे परिणाम की सटीकता 49 से घटकर 38 हो जाती है, और "परिणाम को छोटा करने से रोकने के लिए इसी पैमाने को कम किया जाता है।" इस टिप्पणी से यह स्पष्ट नहीं है कि पैमाना कैसे घटा है, लेकिन हम यह जानते हैं:
तालिका में सूत्र के अनुसार, दो एस को विभाजित करने के बाद आपके पास न्यूनतम संभव पैमाने 6 हो सकते हैं DECIMAL।
इस प्रकार, हम निम्नलिखित परिणामों के साथ समाप्त होते हैं:
e1 / e2
-> Result precision: 49 -> reduced to 38
-> Result scale: 11 -> reduced to 6
Note that 6 is the minimum possible scale it can be reduced to.
It may be between 6 and 11 inclusive.
यह कैसे अंकगणित अतिप्रवाह की व्याख्या करता है
अब उत्तर स्पष्ट है:
हमारे डिवीजन के आउटपुट को कास्ट किया जाता है DECIMAL(38, 6), और DECIMAL(38, 6)10 37 नहीं पकड़ सकता है ।
इसके साथ, हम एक और विभाजन का निर्माण कर सकते हैं जो यह सुनिश्चित करता है कि परिणाम में फिट हो सकता है DECIMAL(38, 6):
DECLARE @big_number DECIMAL(38,0) = '1' + REPLICATE(0, 37);
DECLARE @one_million INT = '1' + REPLICATE(0, 6);
PRINT @big_number / @one_million;
परिणाम है:
10000000000000000000000000000000.000000
दशमलव के बाद 6 शून्य पर ध्यान दें। हम पुष्टि कर सकते हैं कि परिणाम का डेटा प्रकार ऊपर के रूप में DECIMAL(38, 6)उपयोग करके SQL_VARIANT_PROPERTY()है:
DECLARE @big_number DECIMAL(38,0) = '1' + REPLICATE(0, 37);
DECLARE @one_million INT = '1' + REPLICATE(0, 6);
SELECT
SQL_VARIANT_PROPERTY(@big_number / @one_million, 'BaseType') AS [BaseType] -- decimal
, SQL_VARIANT_PROPERTY(@big_number / @one_million, 'Precision') AS [Precision] -- 38
, SQL_VARIANT_PROPERTY(@big_number / @one_million, 'Scale') AS [Scale] -- 6
;
एक खतरनाक कसरत
तो हम इस सीमा के आसपास कैसे पहुँचें?
खैर, यह निश्चित रूप से इस बात पर निर्भर करता है कि आप इन गणनाओं के लिए क्या कर रहे हैं। FLOATगणना के लिए अपनी संख्या बदलने के लिए एक समाधान जिसे आप तुरंत कूद सकते हैं , और फिर DECIMALजब आप कर रहे हों तब उन्हें वापस रूपांतरित करें ।
यह कुछ परिस्थितियों में काम कर सकता है, लेकिन आपको यह समझने में सावधानी बरतनी चाहिए कि वे परिस्थितियां क्या हैं। जैसा कि हम सभी जानते हैं, संख्याओं को और इससे परिवर्तित FLOATकरना खतरनाक है और आपको अप्रत्याशित या गलत परिणाम दे सकता है।
हमारे मामले में, 10 से 37 को परिवर्तित करना और FLOATएक परिणाम मिलता है जो कि केवल गलत है :
DECLARE @big_number DECIMAL(38,0) = '1' + REPLICATE(0, 37);
DECLARE @big_number_f FLOAT = CAST(@big_number AS FLOAT);
SELECT
@big_number AS big_number -- 10^37
, @big_number_f AS big_number_f -- 10^37
, CAST(@big_number_f AS DECIMAL(38, 0)) AS big_number_f_d -- 9999999999999999.5 * 10^21
;
आखिर तुमने इसे हासिल कर ही लिया है। मेरे बच्चों को ध्यान से विभाजित करो।