我在SQL Server
中有一个数据集:
ROW_NUM EMP_ID DATE_KEY TP_DAYS
1 U12345 20131003 1
2 U12345 20131004 0
3 U12345 20131005 0
4 U12345 20131006 0
5 U12345 20150627 1
6 U12345 20150628 0
1 U54321 20131003 1
2 U54321 20131004 0
3 U54321 20131005 0
4 U54321 20131006 0
我需要更新列TP_DAYS
中的所有零,其值增加1到前一个值。
所需的结果集如下:
ROW_NUM EMP_ID DATE_KEY TP_DAYS
1 U12345 20131003 1
2 U12345 20131004 2
3 U12345 20131005 3
4 U12345 20131006 4
5 U12345 20150627 1
6 U12345 20150628 2
1 U54321 20131003 1
2 U54321 20131004 2
3 U54321 20131005 3
4 U54321 20131006 4
我尝试在SQL中使用LAG
和LEAD
函数。但无法达到预期的效果。
有人可以帮助我实现它。
答案 0 :(得分:2)
使用窗口函数(SUM/ROW_NUMBER
,因此它将与SQL Server 2008
一起使用):
WITH cte AS
(
SELECT *, s = SUM(TP_DAYS) OVER(PARTITION BY EMP_ID ORDER BY ROW_NUM)
FROM #tab
), cte2 AS
(
SELECT *,
tp_days_recalculated = ROW_NUMBER() OVER (PARTITION BY EMP_ID, s ORDER BY ROW_NUM)
FROM cte
)
UPDATE cte2
SET TP_DAYS = tp_days_recalculated;
SELECT *
FROM #tab;
的 LiveDemo
强>
输出:
╔═════════╦════════╦══════════╦═════════╗
║ ROW_NUM ║ EMP_ID ║ DATE_KEY ║ TP_DAYS ║
╠═════════╬════════╬══════════╬═════════╣
║ 1 ║ U12345 ║ 20131003 ║ 1 ║
║ 2 ║ U12345 ║ 20131004 ║ 2 ║
║ 3 ║ U12345 ║ 20131005 ║ 3 ║
║ 4 ║ U12345 ║ 20131006 ║ 4 ║
║ 5 ║ U12345 ║ 20150627 ║ 1 ║
║ 6 ║ U12345 ║ 20150628 ║ 2 ║
║ 1 ║ U54321 ║ 20131003 ║ 1 ║
║ 2 ║ U54321 ║ 20131004 ║ 2 ║
║ 3 ║ U54321 ║ 20131005 ║ 3 ║
║ 4 ║ U54321 ║ 20131006 ║ 4 ║
╚═════════╩════════╩══════════╩═════════╝
原始OP问题和示例数据非常明确,tp_days
指标为0
和1
,而不是任何其他值。
特别是Atheer Mostafa:
将此示例检查为证据:http://data.stackexchange.com/stackoverflow/query/edit/423186
这应该是新问题,但我会处理这个案子:
;WITH cte AS
(
SELECT *
,rn = s + ROW_NUMBER() OVER(PARTITION BY EMP_ID, s ORDER BY ROW_NUM) -1
,rnk = DENSE_RANK() OVER(PARTITION BY EMP_ID ORDER BY s)
FROM (SELECT *, s = SUM(tp_days) OVER(PARTITION BY EMP_ID ORDER BY ROW_NUM)
FROM #tab) AS sub
), cte2 AS
(
SELECT c1.*,
tp_days_recalculated = c1.rn - (SELECT COALESCE(MAX(c2.s),0)
FROM cte c2
WHERE c1.emp_id = c2.emp_id
AND c2.rnk = c1.rnk-1)
FROM cte c1
)
UPDATE cte2
SET tp_days = tp_days_recalculated;
的 LiveDemo2
强>
输出:
╔═════════╦════════╦══════════╦═════════╗
║ row_num ║ emp_id ║ date_key ║ tp_days ║
╠═════════╬════════╬══════════╬═════════╣
║ 1 ║ U12345 ║ 20131003 ║ 2 ║
║ 2 ║ U12345 ║ 20131004 ║ 3 ║
║ 3 ║ U12345 ║ 20131005 ║ 4 ║
║ 4 ║ U12345 ║ 20131006 ║ 3 ║
║ 5 ║ U12345 ║ 20150627 ║ 4 ║
║ 6 ║ U12345 ║ 20150628 ║ 5 ║
║ 1 ║ U54321 ║ 20131003 ║ 2 ║
║ 2 ║ U54321 ║ 20131004 ║ 3 ║
║ 3 ║ U54321 ║ 20131005 ║ 1 ║
║ 4 ║ U54321 ║ 20131006 ║ 2 ║
╚═════════╩════════╩══════════╩═════════╝
它不应该将值3,4,2更改为......这就是这种情况。 当我有另一个通用答案时,我不需要你的解决方案,你不告诉我该怎么做...谢谢
Solution mentioned in comment只不过是quirky update
。是的它会起作用,但可能很容易失败:
ORDER BY
,则无法保证稳定的结果相关文章:
答案 1 :(得分:0)
让我假设SQL Server 2012+。您需要识别由1分隔的组。计算组的一种简单方法是通过执行1的累积和。然后row_number()
可用于计算新值。您可以通过可更新的CTE来完成这项工作:
with toupdate as (
select t.*,
row_number() over (partition by empid, grp order by row_num) as new_tp_days
from (select t.*,
sum(tp_days) over (partition by emp_id order by row_num) as grp
from t
) t
)
update toupdate
set tp_days = new_tp_days;
在早期版本的SQL Server中,您可以完成同样的事情(效率较低)。一种方法使用outer apply
。
答案 2 :(得分:-1)
使用简单的代码,我有一个比较简单的方法:
DECLARE @last int=0
UPDATE #Employees set @last=CASE WHEN TP_DAYS=0 THEN @last+1 ELSE TP_DAYS END,
TP_DAYS=CASE WHEN TP_DAYS=0 THEN @last ELSE TP_DAYS END
这可以在任何SQL Server引擎中运行 查看此处的演示