对于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
答案 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/