基于变量执行计算的函数

use*_*140 3 sql-server t-sql sql-server-2008-r2

我看到@ypercube 的回答??这对如何计算似乎是由 3 订购的数量并给出整数答案给出了很好的答案。这让我想到,如果要拆分的次数可变,您将如何处理?

这意味着这是你总是会被 3 分割的 OP(原始海报

Create Table #Orders 
(
    id int IDENTITY(1,1) PRIMARY KEY NOT NULL
    ,partid varchar(100) NOT NULL
    ,qtyordered int DEFAULT '0'
    ,orderedby varchar(100) NOT NULL
    ,ordereddate date DEFAULT GETDATE()
) ;

Insert Into #Orders (partid, qtyordered, orderedby) VALUES
('SS100', 10, 'James'), ('RR200', 5, 'Bob'), ('NN300', 3, 'Jake'), ('OO400', 5, 'Blue') ;

SELECT 
   partid,
   qtyordered,
   [First], 
   [Second],
   [Third] 
FROM 
    #Orders 
  CROSS APPLY
    ( SELECT [Third]  = (qtyordered)                      / 3 )  AS q3
  CROSS APPLY
    ( SELECT [Second] = (qtyordered - [Third])            / 2 )  AS q2
  CROSS APPLY
    ( SELECT [First]  = (qtyordered - [Third] - [Second]) / 1 )  AS q1;
Run Code Online (Sandbox Code Playgroud)

dbfiddle在这里

但是,如果不是总是被 3 分割,而是有一个 int 变量来说明要分割的次数,比如说相同的 DDL 但不是被 3 分割,而是使用

Declare @TTS int = 5
Run Code Online (Sandbox Code Playgroud)

现在您将每个场景拆分为 5 种方式而不是 3 种方式。基本上是一个可重用的函数,可以根据变量“即时”拆分?

RDF*_*ozz 5

基本上,我们只是想将除法中的余数分散到值上。这个过程正是这样做的;它将返回一个表,其中包含拆分的结果 vals。我假设我们不想返回可变数量的列(取决于我们收到的除数),我决定实际生成文本“第一”、“第二”等并不是问题的一部分,所以我在自己的行中返回每个值。

CREATE PROCEDURE split_into_ints(@dividend int, @divisor int)
AS
BEGIN
    WITH result_rows AS
         (SELECT TOP (@divisor) ROW_NUMBER() OVER (ORDER BY o1.object_id)
                 as result_num
            FROM sys.objects o1 CROSS JOIN sys.objects o2
         )
        ,base_vals AS
         (SELECT @dividend / @divisor as base_result
                ,@dividend % @divisor as remainder
         )
    SELECT r.result_num
          ,CASE WHEN r.result_num <= remainder THEN 1 ELSE 0 END
           + v.base_result as result_val
      FROM base_vals v CROSS JOIN result_rows r
     ORDER BY result_num
END;
GO
Run Code Online (Sandbox Code Playgroud)

下列:

EXECUTE split_into_ints 10,3;
EXECUTE split_into_ints 49,6;
EXECUTE split_into_ints 2000,11;
Run Code Online (Sandbox Code Playgroud)

产生这些结果:

result_num           result_val
-------------------- -----------
1                    4
2                    3
3                    3

result_num           result_val
-------------------- -----------
1                    9
2                    8
3                    8
4                    8
5                    8
6                    8

result_num           result_val
-------------------- -----------
1                    182
2                    182
3                    182
4                    182
5                    182
6                    182
7                    182
8                    182
9                    182
10                   181
11                   181
Run Code Online (Sandbox Code Playgroud)

或者,您可以将其设为内联表值函数:

CREATE FUNCTION fn_split_into_ints(@dividend int, @divisor int)
RETURNS TABLE
AS 
RETURN
(
    WITH result_rows AS
         (SELECT TOP (@divisor) ROW_NUMBER() OVER (ORDER BY o1.object_id)
                 as result_num
            FROM sys.objects o1 CROSS JOIN sys.objects o2
         )
        ,base_vals AS
         (SELECT @dividend / @divisor as base_result
                ,@dividend % @divisor as remainder
         )
    SELECT r.result_num
          ,CASE WHEN r.result_num <= remainder THEN 1 ELSE 0 END
           + v.base_result as result_val
      FROM base_vals v CROSS JOIN result_rows r
);
GO
Run Code Online (Sandbox Code Playgroud)

因此,您运行与上述相同的查询:

SELECT * FROM fn_split_into_ints(10,3);
SELECT * FROM fn_split_into_ints(49,6);
SELECT * FROM fn_split_into_ints(2000,11);
Run Code Online (Sandbox Code Playgroud)

结果是一样的。回答一个评论:这将允许您result_val进入一个变量:

SELECT @result_val = result_val
  FROM fn_split_into_ints(49,6)
 WHERE result_num = 6
;
Run Code Online (Sandbox Code Playgroud)

编辑:更改了函数名称,因此函数和过程可以共存(如果您希望它们共存,出于某种原因)。