পাটিগণিত অপারেশনের প্রসঙ্গে যথার্থতা এবং স্কেল বোঝা
আসুন এটি ভাঙ্গা যাক এবং বিভাজন পাটিগণিত অপারেটরের বিশদটি নিবিড়ভাবে দেখুন । বিভাজন অপারেটরের ফলাফলের প্রকার সম্পর্কে এমএসডিএন এর এইটাই বলে :
ফলাফলের প্রকারগুলি
আর্গুমেন্টের ডেটা ধরণের উচ্চতর প্রাধান্য দিয়ে দেয়। আরও তথ্যের জন্য, ডেটা প্রকারের অগ্রাধিকার (লেনদেন-এসকিউএল) দেখুন ।
যদি একটি পূর্ণসংখ্যার লভ্যাংশকে একটি পূর্ণসংখ্যা বিভাজক দ্বারা ভাগ করা হয় তবে ফলাফলটি একটি পূর্ণসংখ্যা হয় যার ফলাফলের কোনও ভগ্নাংশ অংশ কেটে যায়।
আমরা জানি যে @big_number
একটি DECIMAL
। এসকিউএল সার্ভার কোন ডেটা টাইপ 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
আউটপুটে স্কেলের সাথে । এখানে এসকিউএল সার্ভার পাটিগণিত ক্রিয়াকলাপ থেকে প্রাপ্ত ফলাফলের নির্ভুলতা এবং স্কেল কীভাবে নির্ধারণ করে সে সম্পর্কে নিয়মের একটি সারণী এখানে রয়েছে :
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
উপরের টেবিলটিতে নক্ষত্রের মন্তব্য অনুসারে, একটির সর্বাধিক নির্ভুলতা DECIMAL
38 থাকতে পারে । সুতরাং আমাদের ফলাফলের নির্ভুলতা 49 থেকে 38 এ কেটে যায় এবং "ফলাফলের অবিচ্ছেদ্য অংশটি কেটে যাওয়া থেকে রক্ষা করতে সংশ্লিষ্ট স্কেল হ্রাস করা হয়।" স্কেল কীভাবে হ্রাস হবে তা এই মন্তব্য থেকে পরিষ্কার নয় , তবে আমরা এটি জানি:
সারণির সূত্র অনুসারে, দুটি গুলি ভাগ করার পরে আপনার ন্যূনতম সম্ভাব্য স্কেলটি DECIMAL
6 থাকতে পারে।
সুতরাং, আমরা নিম্নলিখিত ফলাফল দিয়ে শেষ:
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
;
একটি বিপজ্জনক workaround
সুতরাং কিভাবে আমরা এই সীমাবদ্ধতা কাছাকাছি পেতে?
ঠিক আছে, এটি অবশ্যই নির্ভর করে আপনি কী জন্য এই গণনা তৈরি করছেন। একটি সমাধান যা আপনি অবিলম্বে ঝাঁপিয়ে যেতে পারেন তা হ'ল আপনার সংখ্যাগুলিকে 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
;
এবং সেখানে আপনি এটা আছে। আমার বাচ্চারা সাবধানে বিভক্ত।