SQL Server 2012基于运行总计的桶

时间:2014-08-13 19:29:45

标签: sql-server-2012 sum running-total

对于SQL Server 2012,我尝试根据存储桶的最大大小(下面示例中为100)和运行总计列来将给定行分配给顺序存储桶。我发现大多数解决方案都是按已知的列更改值进行分区,例按部门ID等分区。但是,在这种情况下我所拥有的是顺序id和大小。我发现的最接近的解决方案在SQL Server 2008的这个主题中进行了讨论,我尝试了但是对于大行集的性能非常慢,比基于游标的解决方案要糟糕得多。 https://dba.stackexchange.com/questions/45179/how-can-i-write-windowing-query-which-sums-a-column-to-create-discrete-buckets

此表最多可包含1000万行。使用SQL Server 2012支持SUM OVER和LAG以及LEAD功能,想知道是否有人可以根据2012年建议解决方案。

CREATE TABLE raw_data (
id    INT PRIMARY KEY
, size  INT NOT NULL
);

INSERT INTO raw_data
(id, size)
VALUES 
  ( 1,    96) -- new bucket here, maximum bucket size is 100
, ( 2,    10) -- and here
, ( 3,    98) -- and here
, ( 4,    20)
, ( 5,    50)
, ( 6,    15)
, ( 7,    97)
, ( 8,    96) -- and here
;

--Expected output
--bucket_size is for illustration only, actual needed output is bucket only

id  size    bucket_size bucket
-----------------------------
1   100     100         1
2    10     10          2
3    98     98          3
4    20     85          4
5    50     85          4
6    15     85          4
7    97     98          5
8    1      98          5

TIA

2 个答案:

答案 0 :(得分:0)

在使用运行总计方法分配存储桶编号之前,您需要生成该bucket_size列,因为数字将根据该列生成。

根据您的预期输出,铲斗范围

1..10
11..85
86..100

您可以使用这样的简单CASE表达式生成bucket_size列,如下例所示:

CASE
    WHEN size <= 10 THEN 10
    WHEN size <= 85 THEN 85
    ELSE 100
END

然后你将使用LAG()来确定一行是否开始属于同一个存储桶的新序列大小:

CASE bucket_size
    WHEN LAG(bucket_size) OVER (ORDER BY id) THEN 0
    ELSE 1
END

这两个计算可以在CROSS APPLY的帮助下在同一个(子)查询中完成:

SELECT
    d.id,
    d.size,
    x.bucket_size,  -- for illustration only
    is_new_seq = CASE x.bucket_size
                     WHEN LAG(x.bucket_size) OVER (ORDER BY d.id) THEN 0
                     ELSE 1
                 END
FROM dbo.raw_data AS d
CROSS APPLY
(
    SELECT
        CASE
            WHEN size <= 10 THEN 10
            WHEN size <= 85 THEN 85
            ELSE 100
        END
) AS x (bucket_size)

以上查询would produce此输出:

id  size  bucket_size  is_new_seq
--  ----  -----------  ----------
1   96    100          1
2   10    10           1
3   98    100          1
4   20    85           1
5   50    85           0
6   15    85           0
7   97    100          1
8   96    100          0

现在将该结果用作派生表,并将SUM()OVER应用于is_new_seq以生成桶号like this

SELECT
    id,
    size,
    bucket = SUM(is_new_seq) OVER (ORDER BY id)
FROM
(
    SELECT
        d.id,
        d.size,
        is_new_seq = CASE x.bucket_size
                         WHEN LAG(x.bucket_size) OVER (ORDER BY d.id) THEN 0
                         ELSE 1
                     END
    FROM dbo.raw_data AS d
    CROSS APPLY
    (
        SELECT
            CASE
                WHEN size <= 10 THEN 10
                WHEN size <= 85 THEN 85
                ELSE 100
            END
    ) AS x (bucket_size)
) AS s
;

答案 1 :(得分:0)

您可以使用窗口功能和框架在SQL Server 2012中轻松实现此目的。语法看起来相当复杂,但概念很简单 - 将所有先前的行加起来并包括当前行。此示例中的cumulative_bucket_size列用于演示目的,因为它是用于派生存储桶编号的等式的一部分:

DECLARE @Bucket_Size AS INT;
SET @Bucket_Size = 100

SELECT
    id,
    size,
    SUM(size) OVER  (
                        PARTITION BY 1 ORDER BY id ASC
                        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
                    ) AS cumulative_bucket_size,
    1 + SUM(size) OVER  (
                        PARTITION BY 1 ORDER BY id ASC
                        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
                    ) / @Bucket_Size AS bucket
FROM
    raw_data

PARTITION BY子句是可选的,但如果列分组有不同的“存储区集”,则会很有用。为了完整起见,我在这里添加了它。

结果:

id  size    cumulative_bucket_size  bucket
------------------------------------------
1   96      96                      1
2   10      106                     2
3   98      204                     3
4   20      224                     3
5   50      274                     3
6   15      289                     3
7   97      386                     4
8   96      482                     5

您可以在以下文章中阅读有关Windows框架的更多信息:

https://www.simple-talk.com/sql/learn-sql-server/window-functions-in-sql-server-part-2-the-frame/