我有一个名为PROD_COST
的表,有5个字段:
(ID, Duration, Cost, COST_NEXT, COST_CHANGE)
我需要额外的字段Groups
进行聚合。
+ ID + Duration + Cost + Next_Cost + Cost_change + Groups+
| 1 | 1 | 10 | 8.5 | -1.5 | 1 |
| 2 | 1 | 8.5 | 12.2 | 3.7 | 2 |
| 3 | 1 | 12.2 | 5.3 | -6.9 | 3 |
| 4 | 1 | 5.3 | 4.2 | 1.2 | 4 |
| 5 | 1 | 4.2 | 6.2 | 2 | 4 |
| 6 | 1 | 6.2 | 9.2 | 3 | 4 |
| 7 | 1 | 9.2 | 7.5 | -2.7 | 5 |
| 8 | 1 | 7.5 | 6.2 | -1.3 | 5 |
| 9 | 1 | 6.2 | 6.3 | 0.1 | 6 |
| 10 | 1 | 6.3 | 7.2 | 0.9 | 6 |
| 11 | 1 | 7.2 | 7.5 | 0.3 | 6 |
| 12 | 1 | 7.5 | 0 | 7.5 | 6 |
+----+----------+------+-----------+-------------+-------+
我需要按Groups
对Cost_change
字段进行分组。它可以是正数,负数或零。
有些人建议我使用这段代码:
Select
id
, COST_CHANGE
, sum(Groups) over (order by id asc) +1
from
(
select
pc.*,
(case when sign(cost_change) - sign(lag(cost_change) over (order by id)) between -1 and 1
then 0
else 1 -- `NULL` intentionally goes here
end) Groups
from Prod_Cost
) pc
但是有一个问题:如果两个正/负或负/正值之间有0个值,那么它将它们组合在一起,例如:
Cost_change Groups
| -5.279 | 33 |
| 5.279 | 34 |
| 0.000 | 34 |
| -5.279 | 34 |
| 0.000 | 34 |
| 5.279 | 34 |
| -8.769 | 35 |
我需要:
Cost_change Groups
| -5.279 | 33 |
| 5.279 | 34 |
| 0.000 | 34 |
| -5.279 | 35 |
| 0.000 | 35 |
| 5.279 | 36 |
| -8.769 | 37 |
第二个例子:
Cost_change Groups
| 7.574 | 68 |
| 0.000 | 68 |
| -5.279 | 68 |
| -3.490 | 68 |
但我需要:
Cost_change Groups
| 7.574 | 68 |
| 0.000 | 68 |
| -5.279 | 69 |
| -3.490 | 69 |
我将非常感谢任何帮助。
答案 0 :(得分:1)
以下是SQL Fiddle解决方案。
我会专注于您的问题,并在表格中仅保留相关列:ID
和Cost_change
。
让我们创建一些涵盖所有可能情况的样本数据。我在不同的地方添加了几个零:
DECLARE @T TABLE (ID int IDENTITY(1,1), Cost_change decimal(10,3));
INSERT INTO @T (Cost_change) VALUES
( 0.000),
( 0.000),
( -1.5),
( 3.7),
( -6.9),
( 1.2),
( 0.000),
( 0.000),
( 2.0),
( 3.0),
( -2.7),
( 0.000),
( -1.3),
( 0.1),
( 0.9),
( 0.3),
( 7.5),
(-5.279),
( 5.279),
( 0.000),
(-5.279),
( 0.000),
( 5.279),
(-8.769),
( 7.574),
( 0.000),
(-5.279),
(-3.490),
(-5.279),
( 5.279),
( 0.000),
( 0.000),
( 0.000),
(-5.279),
( 0.000),
( 0.000),
( 5.279),
(-8.769),
( 7.574),
( 0.000),
( 0.000),
( 0.000),
( 0.000),
(-5.279),
(-3.490);
我会明确地说出每一步,这样会更容易理解。
处理数据将有两个主要部分。首先,我们将处理所有非零值并为它们生成忽略零值的组号。然后,对于每个零值,我们将在生成的中找到相应的组号。
CTE_Signs
使用sign
函数计算当前行和上一行Cost_change
的{{1}}。请注意,我们在这里过滤掉零值。
LAG
比较当前行和上一行的符号,如果它们不同,则将CTE_Changes
设置为1。第一行有Change
作为上一行的符号,NULL
会处理它。
ISNULL
计算CTE_Groups
的运行总和,生成的组号随每个Change
而增加。
这将为我们提供所有非零值的正确组号。
第二个主要部分是使用Change
获取所有零值并为它们找到正确的组号。最后OUTER APPLY
两个部分。
UNION ALL
<强>结果强>
WITH
CTE_Signs
AS
(
SELECT *
,SIGN(Cost_change) AS SignCurr
,SIGN(LAG(Cost_change) OVER (ORDER BY ID)) AS SignPrev
FROM @T
WHERE Cost_change <> 0
)
,CTE_Changes
AS
(
SELECT *
, CASE WHEN SignCurr <> ISNULL(SignPrev, SignCurr) THEN 1 ELSE 0 END AS Change
FROM CTE_Signs
)
,CTE_Groups
AS
(
SELECT *
, SUM(Change) OVER (ORDER BY ID) AS Groups
FROM CTE_Changes
)
SELECT TT.ID, TT.Cost_change, ISNULL(CA.Groups, 0) AS Groups
FROM
@T AS TT
OUTER APPLY
(
SELECT TOP(1) CTE_Groups.Groups
FROM CTE_Groups
WHERE CTE_Groups.ID < TT.ID
ORDER BY CTE_Groups.ID DESC
) AS CA
WHERE
TT.Cost_change = 0
UNION ALL
SELECT ID, Cost_change, Groups
FROM CTE_Groups
ORDER BY ID;
答案 1 :(得分:0)
如果该表不是太大,您可以尝试递归cte:
create table t(id int, v int)
insert into t values
(1, -4),
(2, -3),
(3, 0),
(4, 0),
(5, 1),
(6, 2),
(7, -7),
(8, 9),
(9, 0),
(10, -5)
;with cte as
(select *, 1 as gr, case when v <> 0 then v end pr
from t where id = 1
union all
select t.*, c.gr + case when t.v = 0 or(t.v*c.v > 0) then 0 else 1 end,
case when t.v <> 0 then t.v else c.pr end
from cte c
join t on c.id + 1 = t.id
)
select * from cte
order by id
http://sqlfiddle.com/#!3/92b11/1
或者,如果分组数量可能不是正数或连续数:
;with cte as(select *, ( select case when t.v <> 0 then t.v
else(select top 1 ti.v from t ti
where ti.id < t.id and ti.v <> 0
order by ti.id desc)
end) nv
from t)
select *,
row_number() over(order by case when nv < 0 then 1 else 2 end),
id - row_number() over(order by case when nv < 0 then 1 else 2 end, id)
from cte
order by id