محاسبات پلکانی در SQL SERVER
شرایطی را در نظر بگیرید که مودی قبلا ساختی داشته است و عوارض مربوط به آن ساخت را پرداخت کرده است. عوارض ساخت جدید برای آن به صورت پلکانی محاسبه شود
مساحت ناخالص/کل: S_total
مساحت موجود/ساخت قبل: S_exist
پلکان محاسبه
0تا200 متر مربع
200 تا 250
250 تا 300
300 تا 400
400 تا 500
500 تا 600
و 600 به بالا
اگر ساخت قبلی کمتر از 200 مثلا 160 متر مربع باشد. و ساخت کل مثلا 670 متر
پله های عوارض به این صورت محاسبه خواهد شد
200 - 160 = 40
250 - 200 = 50
300 - 250 = 50
400 - 300 = 100
500 - 400 = 100
600 - 500 = 100
670 - 600 = 70
اگر ساخت قبلی کمتر از 400 مثلا 360 متر مربع باشد. و ساخت کل مثلا 570 متر
پله های عوارض به این صورت محاسبه خواهد شد
200 - 200 = 0
250 - 250 = 0
300 - 300 = 0
400 - 360 = 40
500 - 400 = 100
570 - 500 = 70
600 - 600 = 0
می خواهیم تابع SQL بنویسیم که این شرایط را شامل شود
ALTER FUNCTION [dbo].[FS100] ( @S_total AS FLOAT, -- مساحت کل @S_exist AS FLOAT, -- مساحت موجود @C AS SMALLINT -- پله ی مساحت ) RETURNS FLOAT AS BEGIN DECLARE @B FLOAT BEGIN IF @S_total <= 200 BEGIN IF @S_exist < = 200 BEGIN SET @B = CASE @C WHEN 1 THEN @S_total - @S_exist --WHEN 2 THEN 50 --WHEN 3 THEN 50 --WHEN 4 THEN 100 --WHEN 5 THEN 100 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END END -- --============================================ ELSE IF @S_total <= 250 BEGIN IF @S_exist < = 200 BEGIN SET @B = CASE @C WHEN 1 THEN 200 - @S_exist WHEN 2 THEN @S_total - 200 --WHEN 3 THEN 50 --WHEN 4 THEN 100 --WHEN 5 THEN 100 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END ELSE -- @S_exist < = 250 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN @S_total - @S_exist --WHEN 3 THEN 50 --WHEN 4 THEN 100 --WHEN 5 THEN 100 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END END --============================================ ELSE IF @S_total <= 300 BEGIN IF @S_exist < = 200 BEGIN SET @B = CASE @C WHEN 1 THEN 200 - @S_exist WHEN 2 THEN 250 - 200 WHEN 3 THEN @S_total - 250 --WHEN 4 THEN 100 --WHEN 5 THEN 100 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END ELSE IF @S_exist < = 250 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 250 - @S_exist WHEN 3 THEN @S_total - 250 --WHEN 4 THEN 100 --WHEN 5 THEN 100 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END ELSE --IF @S_exist < = 300 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN @S_total - @S_exist --WHEN 4 THEN 100 --WHEN 5 THEN 100 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END END -- --============================================ ELSE IF @S_total <= 400 BEGIN IF @S_exist < = 200 BEGIN SET @B = CASE @C WHEN 1 THEN 200 - @S_exist WHEN 2 THEN 250 - 200 WHEN 3 THEN 300 - 250 WHEN 4 THEN @S_total - 300 --WHEN 5 THEN 100 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END ELSE IF @S_exist < = 250 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 250 - 200 WHEN 3 THEN 300 - 250 WHEN 4 THEN @S_total - 300 --WHEN 5 THEN 100 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END ELSE IF @S_exist < = 300 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 300 - 250 WHEN 4 THEN @S_total - @S_exist --WHEN 5 THEN 100 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END ELSE --IF @S_exist < = 400 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 0 WHEN 4 THEN @S_total - @S_exist --WHEN 5 THEN 100 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END END -- --============================================ ELSE IF @S_total <= 500 BEGIN IF @S_exist < = 200 BEGIN SET @B = CASE @C WHEN 1 THEN 200 - @S_exist WHEN 2 THEN 250 - 200 WHEN 3 THEN 300 - 250 WHEN 4 THEN 400 - 300 WHEN 5 THEN @S_total - 400 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END ELSE IF @S_exist < = 250 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 250 - @S_exist WHEN 3 THEN 300 - 250 WHEN 4 THEN 400 - 300 WHEN 5 THEN @S_total - 400 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END ELSE IF @S_exist < = 300 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 300 - @S_exist WHEN 4 THEN 400 - 300 WHEN 5 THEN @S_total - 400 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END ELSE IF @S_exist < = 400 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 0 WHEN 4 THEN 400 - @S_exist WHEN 5 THEN @S_total - 400 --WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END ELSE --IF @S_exist < = 500 SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 0 WHEN 4 THEN 0 WHEN 5 THEN @S_total - @S_exist -- WHEN 6 THEN 100 --ELSE @S_total - @S_exist END END -- --============================================ ELSE IF @S_total <= 600 BEGIN IF @S_exist < = 200 BEGIN SET @B = CASE @C WHEN 1 THEN 200 - @S_exist WHEN 2 THEN 250 - 200 WHEN 3 THEN 300 - 250 WHEN 4 THEN 400 - 300 WHEN 5 THEN 500 - 400 WHEN 6 THEN @S_total - 500 --ELSE @S_total - @S_exist END END ELSE IF @S_exist < = 250 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 250 - @S_exist WHEN 3 THEN 300 - 250 WHEN 4 THEN 400 - 300 WHEN 5 THEN 500 - 400 WHEN 6 THEN @S_total - 500 --ELSE @S_total - @S_exist END END ELSE IF @S_exist < = 300 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 300 - @S_exist WHEN 4 THEN 400 - 300 WHEN 5 THEN 500 - 400 WHEN 6 THEN @S_total - 500 --ELSE @S_total - @S_exist END END ELSE IF @S_exist < = 400 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 0 WHEN 4 THEN 400 - @S_exist WHEN 5 THEN 500 - 400 WHEN 6 THEN @S_total - 500 --ELSE @S_total - @S_exist END END ELSE IF @S_exist < = 500 SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 0 WHEN 4 THEN 0 WHEN 5 THEN 500 - @S_exist WHEN 6 THEN @S_total - 500 --ELSE @S_total - @S_exist END ELSE --IF @S_exist < = 600 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 0 WHEN 4 THEN 0 WHEN 5 THEN 0 WHEN 6 THEN @S_total - @S_exist --ELSE @S_total - @S_exist END END END -- --============================================ ELSE --> 600 BEGIN IF @S_exist < = 200 BEGIN SET @B = CASE @C WHEN 1 THEN 200 - @S_exist WHEN 2 THEN 250 - 200 WHEN 3 THEN 300 - 250 WHEN 4 THEN 400 - 300 WHEN 5 THEN 500 - 400 WHEN 6 THEN 600 - 500 ELSE @S_total - 600 END END ELSE IF @S_exist < = 250 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 250 - @S_exist WHEN 3 THEN 300 - 250 WHEN 4 THEN 400 - 300 WHEN 5 THEN 500 - 400 WHEN 6 THEN 600 - 500 ELSE @S_total - 600 END END ELSE IF @S_exist < = 300 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 300 - @S_exist WHEN 4 THEN 400 - 300 WHEN 5 THEN 500 - 400 WHEN 6 THEN 600 - 500 ELSE @S_total - 600 END END ELSE IF @S_exist < = 400 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 0 WHEN 4 THEN 400 - @S_exist WHEN 5 THEN 500 - 400 WHEN 6 THEN 600 - 500 ELSE @S_total - 600 END END ELSE IF @S_exist < = 500 SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 0 WHEN 4 THEN 0 WHEN 5 THEN 500 - @S_exist WHEN 6 THEN 600 - 500 ELSE @S_total - 600 END ELSE IF @S_exist < = 600 BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 0 WHEN 4 THEN 0 WHEN 5 THEN 0 WHEN 6 THEN 600 - @S_exist ELSE @S_total - 600 END END ELSE BEGIN SET @B = CASE @C WHEN 1 THEN 0 WHEN 2 THEN 0 WHEN 3 THEN 0 WHEN 4 THEN 0 WHEN 5 THEN 0 WHEN 6 THEN 0 ELSE @S_total - @S_exist END END END END RETURN @B END
----------------------------------------------------------------------------------------------
اما نظر دیپ سیک وقتی ازش پرسیدم این تابع منو یاد ماتریس پایین مثلثی می اندازه:
کد تابعی که نوشتید واقعاً شبیه یک ماتریس پایینمثلثی (lower triangular matrix) است. در واقع منطق کار به این شکل است:
تحلیل ساختار ماتریس پایینمثلثی در کد شما:
1. پارامترهای ورودی:
-
@S_total: مقدار اصلی (مثلاً مساحت یا درآمد) -
@S_exist: مقدار پایه یا مرجع -
@C: شماره پله یا سطر (1 تا 7)
2. ساختار ماتریسگونه:
هر بلوک ELSE IF @S_total <= X مانند یک سطر از ماتریس عمل میکند، و شرطهای داخلی IF @S_exist <= Y مانند ستونها هستند.
3. مقادیر ثابت (نقطههای شکست):
200, 250, 300, 400, 500, 600
این مقادیر مرزهای هر پله را تعیین میکنند.
4. منطق محاسباتی:
تابع در واقع تفاضل بخشبندی شده بین @S_total و @S_exist را محاسبه میکند:
-
اگر
@C = 1: فقط تفاضل در پله اول (0-200) محاسبه میشود -
اگر
@C = 2: فقط تفاضل در پله دوم (200-250) محاسبه میشود -
و به همین ترتیب...
5. ماتریس پایینمثلثی:
اگر این مقادیر را به صورت ماتریس نمایش دهیم:
P12\P13 <=200 <=250 <=300 <=400 <=500 <=600 >600 <=200 Δ - - - - - - <=250 Δ Δ - - - - - <=300 Δ Δ Δ - - - - <=400 Δ Δ Δ Δ - - - <=500 Δ Δ Δ Δ Δ - - <=600 Δ Δ Δ Δ Δ Δ - >600 Δ Δ Δ Δ Δ Δ Δ
که در آن:
-
Δ = مقدار غیرصفر (محاسبه تفاضل)
-
- = مقدار صفر (چون
@S_existاز@S_totalبزرگتر است)
پیشنهاد بهینهسازی:
اگر تعداد پلهها زیاد شود، این کد بسیار پیچیده خواهد شد. میتوانید از یک جدول پلکانی استفاده کنید:
CREATE TABLE TaxBrackets ( BracketOrder INT PRIMARY KEY, MinValue FLOAT, MaxValue FLOAT ); INSERT INTO TaxBrackets VALUES (1, 0, 200), (2, 200, 250), (3, 250, 300), (4, 300, 400), (5, 400, 500), (6, 500, 600), (7, 600, NULL);
سپس تابع را سادهتر کنید.
کاربرد عملی:
این نوع تابع معمولاً برای محاسبات پلکانی مالیاتی، تعرفهبندی تصاعدی یا محاسبه پاداش بر اساس بازهها استفاده میشود.
--------------
این هم تابع ساده تر و جمع و جور تر
ALTER FUNCTION [dbo].[FS100]
(
@S_total FLOAT,
@S_exist FLOAT,
@C SMALLINT
)
RETURNS FLOAT
AS
BEGIN
DECLARE
@StepStart FLOAT,
@StepEnd FLOAT,
@Result FLOAT;
/* Define step boundaries */
SET @StepStart =
CASE @C
WHEN 1 THEN 0
WHEN 2 THEN 200
WHEN 3 THEN 250
WHEN 4 THEN 300
WHEN 5 THEN 400
WHEN 6 THEN 500
ELSE 600
END;
SET @StepEnd =
CASE @C
WHEN 1 THEN 200
WHEN 2 THEN 250
WHEN 3 THEN 300
WHEN 4 THEN 400
WHEN 5 THEN 500
WHEN 6 THEN 600
ELSE @S_total -- open-ended last step
END;
/* Calculate overlap */
SET @Result =
CASE
WHEN @S_total <= @StepStart OR @S_exist >= @StepEnd THEN 0
ELSE
(CASE WHEN @S_total < @StepEnd THEN @S_total ELSE @StepEnd END)
- (CASE WHEN @S_exist > @StepStart THEN @S_exist ELSE @StepStart END)
END;
RETURN @Result;
END