我必须显示动态列,其中qty的总和等于1000.直到第一个边界,这些行显示为COL1
。之后从该行开始,逐行添加数量,直到总和再次为1000。然后这些显示在COL2
中。然后是COL3
的相同程序,依此类推。
这是我的源表......
ID QTY
-------------
1 240
2 101
3 43
4 43
5 24
6 43
7 59
8 11
9 65
10 200
11 16
12 1
13 195
14 50
15 40
预期产出:
ID COL1 COL2
1 240 0
2 101 0
3 43 0
4 43 0
5 24 0
6 43 0
7 59 0
8 11 0
9 65 0
10 200 0
11 16 0
12 1 0
13 154 41
14 0 50
15 0 40
答案 0 :(得分:2)
您可能已经猜到这是通过使用动态SQL完成的。采取以下步骤:
#staging
的所有列填充临时表(SUM
):SUM(qty) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
。这样做是为了能够在下一步中使用LAG
。原因:您不能在另一个窗口函数上设置窗口函数。LAG
函数来查看是否越过了上边界或下边界。该过程使用以下结构:
SUM() OVER(...)
,LAG(...) OVER(...)
和ROW_NUMBER() OVER(...)
。FOR XML PATH('')
用于字符串连接。PS:我将样本数据加倍,以测试三列。
剧本:
CREATE TABLE #tt(id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,qty INT NOT NULL);
INSERT INTO #tt(qty)VALUES
(240),(101),(43),(43),(24),(43),(59),(11),(65),(200),(16),(1),(195),(50),(40),(240),(101),(43),(43),(24),(43),(59),(11),(65),(200),(16),(1),(195),(50),(40);
DECLARE @tot_cols INT=(CASE WHEN 0=(SELECT SUM(qty) FROM #tt) THEN 1 ELSE (SELECT SUM(qty) FROM #tt) END-1)/1000+1; -- 0 .. 1000 // 1001 .. 2000 // ...
DECLARE @staging_cols NVARCHAR(MAX)=(
SELECT N',col'+n+N'=SUM(qty) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)'
FROM (
SELECT TOP (@tot_cols) CAST(ROW_NUMBER() OVER(ORDER BY t1.number) AS VARCHAR) AS n
FROM master.dbo.spt_values AS t1 CROSS JOIN master.dbo.spt_values AS t2
) AS tally
WHERE n<=@tot_cols
FOR XML PATH('')
);
DECLARE @staging_create NVARCHAR(MAX)=
N'SELECT id,qty'+@staging_cols+' INTO #staging FROM #tt;';
DECLARE @select_cols NVARCHAR(MAX)=(
SELECT N',col'+n+N'='+
N'CASE WHEN col'+n+N'>'+b_upper+N' ' +
N'THEN CASE WHEN LAG(col'+n+N') OVER (ORDER BY id)<='+b_upper+N' '+
N'THEN '+b_upper+N'-LAG(col'+n+N') OVER (ORDER BY id) '+
N'ELSE 0 '+
N'END ' +
N'WHEN col'+n+N'>'+b_lower+N' '+
N'THEN CASE WHEN LAG(col'+n+N') OVER (ORDER BY id)<='+b_lower+N' '+
N'THEN col'+n+N'-'+b_lower+N' '+
N'ELSE qty ' +
N'END ' +
N'ELSE 0 '+
N'END'
FROM (
SELECT TOP (@tot_cols) CAST(ROW_NUMBER() OVER(ORDER BY t1.number) AS VARCHAR) AS n
FROM master.dbo.spt_values t1 CROSS JOIN master.dbo.spt_values t2
) AS tally
CROSS APPLY (
SELECT b_lower=CAST(CASE WHEN n=1 THEN 0 ELSE (n-1)*1000 END AS VARCHAR),
b_upper=CAST(n*1000 AS VARCHAR)
) AS boundaries
WHERE n<=@tot_cols
FOR XML PATH('')
);
DECLARE @select_result NVARCHAR(MAX)=
N'SELECT id'+REPLACE(REPLACE(@select_cols,N'<',N'<'),N'>',N'>')+N' FROM #staging ORDER BY id;';
DECLARE @stmt NVARCHAR(MAX)=@staging_create+@select_result+'DROP TABLE #staging;';
EXEC sp_executesql @stmt;
DROP TABLE #tt;
这具有相同的基础知识(具有累积和的临时表,滞后以与之前的值进行比较,计数表...)但使用其他构造。
2008R2不支持SUM()
窗口,不支持LAG
。累积总和使用CURSOR
(2008R2中唯一合理的方法)完成;滞后是通过将登台表自我链接到先前的id来完成的。这是:
CREATE TABLE #tt(id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,qty INT NOT NULL);
INSERT INTO #tt(qty)VALUES
(240),(101),(43),(43),(24),(43),(59),(11),(65),(200),(16),(1),(195),(50),(40),(240),(101),(43),(43),(24),(43),(59),(11),(65),(200),(16),(1),(195),(50),(40);
DECLARE @tot_cols INT=(CASE WHEN 0=(SELECT SUM(qty) FROM #tt) THEN 1 ELSE (SELECT SUM(qty) FROM #tt) END-1)/1000+1; -- 0 .. 1000 // 1001 .. 2000 // ...
DECLARE @staging_cols NVARCHAR(MAX)=(
SELECT N',col'+n+N'=0'
FROM (
SELECT TOP (@tot_cols) CAST(ROW_NUMBER() OVER(ORDER BY t1.number) AS VARCHAR) AS n
FROM master.dbo.spt_values AS t1 CROSS JOIN master.dbo.spt_values AS t2
) AS tally
WHERE n<=@tot_cols
FOR XML PATH('')
);
DECLARE @staging_create NVARCHAR(MAX)=
N'SELECT id,qty'+@staging_cols+' INTO #staging FROM #tt;ALTER TABLE #staging ADD CONSTRAINT PK_staging PRIMARY KEY CLUSTERED (id);';
DECLARE @spillover_cols_sel NVARCHAR(MAX)=STUFF(REPLACE(@staging_cols,N'=0',N''),1,1,N'');
DECLARE @spillover_cols_upd NVARCHAR(MAX)=STUFF(REPLACE(@staging_cols,N'=0',N'=@tot_qty'),1,1,N'');
DECLARE @staging_fill NVARCHAR(MAX)=
N'DECLARE c_s CURSOR FOR SELECT qty FROM #staging ORDER BY id FOR UPDATE OF '+@spillover_cols_sel+';'+
N'OPEN c_s; DECLARE @qty INT; DECLARE @tot_qty INT; SET @tot_qty=0; WHILE 1=1 BEGIN '+
N'FETCH NEXT FROM c_s INTO @qty; IF @@FETCH_STATUS<>0 BREAK; SET @tot_qty=@tot_qty+@qty;'+
N'UPDATE #staging SET '+@spillover_cols_upd+' WHERE CURRENT OF c_s;'+
N'END CLOSE c_s;DEALLOCATE c_s;';
DECLARE @select_cols NVARCHAR(MAX)=(
SELECT N',col'+n+N'='+
N'CASE WHEN bt.col'+n+N'>'+b_upper+N' ' +
N'THEN CASE WHEN ISNULL(lt.col'+n+N',0)<='+b_upper+N' '+
N'THEN '+b_upper+N'-ISNULL(lt.col'+n+N',0) '+
N'ELSE 0 '+
N'END ' +
N'WHEN bt.col'+n+N'>'+b_lower+N' '+
N'THEN CASE WHEN ISNULL(lt.col'+n+N',0)<='+b_lower+N' '+
N'THEN bt.col'+n+N'-'+b_lower+N' '+
N'ELSE bt.qty ' +
N'END ' +
N'ELSE 0 '+
N'END'
FROM (
SELECT TOP (@tot_cols) CAST(ROW_NUMBER() OVER(ORDER BY t1.number) AS VARCHAR) AS n
FROM master.dbo.spt_values t1 CROSS JOIN master.dbo.spt_values t2
) AS tally
CROSS APPLY (
SELECT b_lower=CAST(CASE WHEN n=1 THEN 0 ELSE (n-1)*1000 END AS VARCHAR),
b_upper=CAST(n*1000 AS VARCHAR)
) AS boundaries
WHERE n<=@tot_cols
FOR XML PATH('')
);
DECLARE @select_result NVARCHAR(MAX)=
N'SELECT bt.id'+REPLACE(REPLACE(@select_cols,N'<',N'<'),N'>',N'>')+N' '+
N'FROM #staging AS bt LEFT JOIN #staging AS lt ON lt.id=bt.id-1 ORDER BY bt.id;';
DECLARE @stmt NVARCHAR(MAX)=@staging_create+@staging_fill+@select_result+'DROP TABLE #staging;';
EXEC sp_executesql @stmt;
DROP TABLE #tt;
两个脚本的结果:
+----+------+------+------+
| id | col1 | col2 | col3 |
+----+------+------+------+
| 1 | 240 | 0 | 0 |
| 2 | 101 | 0 | 0 |
| 3 | 43 | 0 | 0 |
| 4 | 43 | 0 | 0 |
| 5 | 24 | 0 | 0 |
| 6 | 43 | 0 | 0 |
| 7 | 59 | 0 | 0 |
| 8 | 11 | 0 | 0 |
| 9 | 65 | 0 | 0 |
| 10 | 200 | 0 | 0 |
| 11 | 16 | 0 | 0 |
| 12 | 1 | 0 | 0 |
| 13 | 154 | 41 | 0 |
| 14 | 0 | 50 | 0 |
| 15 | 0 | 40 | 0 |
| 16 | 0 | 240 | 0 |
| 17 | 0 | 101 | 0 |
| 18 | 0 | 43 | 0 |
| 19 | 0 | 43 | 0 |
| 20 | 0 | 24 | 0 |
| 21 | 0 | 43 | 0 |
| 22 | 0 | 59 | 0 |
| 23 | 0 | 11 | 0 |
| 24 | 0 | 65 | 0 |
| 25 | 0 | 200 | 0 |
| 26 | 0 | 16 | 0 |
| 27 | 0 | 1 | 0 |
| 28 | 0 | 23 | 172 |
| 29 | 0 | 0 | 50 |
| 30 | 0 | 0 | 40 |
+----+------+------+------+