不熟悉 SQL 运行 完整解决方案的 UPDATE 语句
Unfamiliar SQL UPDATE statement for running total solution
我遇到了以下 SQL UPDATE 语句,该语句计算 table 中的 运行 总列:
UPDATE N1 SET
RunningTotal = (SELECT SUM (SubTotal)
FROM #Sales X1
WHERE
N1.FiscalYear = X1.FiscalYear AND
X1.OrderNumber <= N1.OrderNumber)
FROM
#Sales N1
我以前没有见过这种模式,而且由于搜索 SQL 语句的困难,我一直没能找到解释。具体来说,我想知道上面的语句是如何更新整个 table;循环是怎么发生的?
注意:该语句工作正常;在 SSMS 中看到的前后结果如下:
(我在 Windows 10 x64 上使用 SQL Server 2017 社区版。)
您的更新语句只是对当前 OrderNumber 之前的所有行求和。
让我告诉你另一种方法:
create table tbl (FiscalYear int, OrderDate date, OrderNumber int, SubTotal decimal(10,2), RunningTotal decimal(10,2));
insert into tbl values
(2011, '20110531', 1, 5000.02, null),
(2011, '20110531', 2, 1000.15, null),
(2011, '20110531', 3, 700.25, null),
(2011, '20110531', 4, 225.02, null),
(2011, '20110531', 5, 1258.25, null),
(2011, '20110531', 6, 1000.00, null),
(2011, '20110531', 7, 695.20, null),
(2011, '20110531', 8, 789.25, null),
(2011, '20110531', 9, 2125.02, null);
GO
CTE 查询计算累计总数,第二个查询更新您的 table。
;with x as
(
select FiscalYear,
OrderDate,
OrderNumber,
SubTotal,
sum(SubTotal) over (partition by FiscalYear
order by FiscalYear, OrderDate, OrderNumber) as CumTotal
from tbl
)
update t
set RunningTotal = CumTotal
from tbl t
join x
on x.Fiscalyear = t.FiscalYear
and x.OrderDate = t.OrderDate
and x.OrderNumber = t.OrderNumber;
GO
9 rows affected
select * from tbl;
GO
FiscalYear | OrderDate | OrderNumber | SubTotal | RunningTotal
---------: | :------------------ | ----------: | :------- | :-----------
2011 | 31/05/2011 00:00:00 | 1 | 5000.02 | 5000.02
2011 | 31/05/2011 00:00:00 | 2 | 1000.15 | 6000.17
2011 | 31/05/2011 00:00:00 | 3 | 700.25 | 6700.42
2011 | 31/05/2011 00:00:00 | 4 | 225.02 | 6925.44
2011 | 31/05/2011 00:00:00 | 5 | 1258.25 | 8183.69
2011 | 31/05/2011 00:00:00 | 6 | 1000.00 | 9183.69
2011 | 31/05/2011 00:00:00 | 7 | 695.20 | 9878.89
2011 | 31/05/2011 00:00:00 | 8 | 789.25 | 10668.14
2011 | 31/05/2011 00:00:00 | 9 | 2125.02 | 12793.16
db<>fiddle here
这不是真正的循环,但我会解释一下:
UPDATE N1 SET
RunningTotal = ...
FROM #Sales N1
因此,它正在更新整个 table。更新没有 where 子句,因此无论如何都会更新每一行。我个人更喜欢这种为 table 添加别名并对别名使用 UPDATE 的风格,因为当您有复杂的更新时,它可以使查看更改更容易。
内部:
SELECT SUM (SubTotal)
FROM #Sales X1
WHERE N1.FiscalYear = X1.FiscalYear AND
X1.OrderNumber <= N1.OrderNumber
获取正在处理的订单之前和等于该订单的每年销售额的总和。它不是在代码中循环;它实际上是 运行 每行或返回数据的子查询。
您的问题的最佳解决方案是可更新的 CTE:
WITH toupdate AS
(SELECT S.*,
SUM(SubTotal) OVER (PARTITION BY FiscalYear ORDER BY OrderNumber) AS new_RunningTotal
FROM #Sales S
)
UPDATE toupdate
SET RunningTotal = new_RunningTotal;
这没有联接或相关子查询。通常,window 函数将比相关子查询等效函数快得多。可更新的 CTE 是 SQL 服务器的一项非常好的功能,可以让您免于额外的 JOIN
。
我遇到了以下 SQL UPDATE 语句,该语句计算 table 中的 运行 总列:
UPDATE N1 SET
RunningTotal = (SELECT SUM (SubTotal)
FROM #Sales X1
WHERE
N1.FiscalYear = X1.FiscalYear AND
X1.OrderNumber <= N1.OrderNumber)
FROM
#Sales N1
我以前没有见过这种模式,而且由于搜索 SQL 语句的困难,我一直没能找到解释。具体来说,我想知道上面的语句是如何更新整个 table;循环是怎么发生的?
注意:该语句工作正常;在 SSMS 中看到的前后结果如下:
(我在 Windows 10 x64 上使用 SQL Server 2017 社区版。)
您的更新语句只是对当前 OrderNumber 之前的所有行求和。
让我告诉你另一种方法:
create table tbl (FiscalYear int, OrderDate date, OrderNumber int, SubTotal decimal(10,2), RunningTotal decimal(10,2)); insert into tbl values (2011, '20110531', 1, 5000.02, null), (2011, '20110531', 2, 1000.15, null), (2011, '20110531', 3, 700.25, null), (2011, '20110531', 4, 225.02, null), (2011, '20110531', 5, 1258.25, null), (2011, '20110531', 6, 1000.00, null), (2011, '20110531', 7, 695.20, null), (2011, '20110531', 8, 789.25, null), (2011, '20110531', 9, 2125.02, null); GO
CTE 查询计算累计总数,第二个查询更新您的 table。
;with x as ( select FiscalYear, OrderDate, OrderNumber, SubTotal, sum(SubTotal) over (partition by FiscalYear order by FiscalYear, OrderDate, OrderNumber) as CumTotal from tbl ) update t set RunningTotal = CumTotal from tbl t join x on x.Fiscalyear = t.FiscalYear and x.OrderDate = t.OrderDate and x.OrderNumber = t.OrderNumber; GO
9 rows affected
select * from tbl; GO
FiscalYear | OrderDate | OrderNumber | SubTotal | RunningTotal ---------: | :------------------ | ----------: | :------- | :----------- 2011 | 31/05/2011 00:00:00 | 1 | 5000.02 | 5000.02 2011 | 31/05/2011 00:00:00 | 2 | 1000.15 | 6000.17 2011 | 31/05/2011 00:00:00 | 3 | 700.25 | 6700.42 2011 | 31/05/2011 00:00:00 | 4 | 225.02 | 6925.44 2011 | 31/05/2011 00:00:00 | 5 | 1258.25 | 8183.69 2011 | 31/05/2011 00:00:00 | 6 | 1000.00 | 9183.69 2011 | 31/05/2011 00:00:00 | 7 | 695.20 | 9878.89 2011 | 31/05/2011 00:00:00 | 8 | 789.25 | 10668.14 2011 | 31/05/2011 00:00:00 | 9 | 2125.02 | 12793.16
db<>fiddle here
这不是真正的循环,但我会解释一下:
UPDATE N1 SET
RunningTotal = ...
FROM #Sales N1
因此,它正在更新整个 table。更新没有 where 子句,因此无论如何都会更新每一行。我个人更喜欢这种为 table 添加别名并对别名使用 UPDATE 的风格,因为当您有复杂的更新时,它可以使查看更改更容易。
内部:
SELECT SUM (SubTotal)
FROM #Sales X1
WHERE N1.FiscalYear = X1.FiscalYear AND
X1.OrderNumber <= N1.OrderNumber
获取正在处理的订单之前和等于该订单的每年销售额的总和。它不是在代码中循环;它实际上是 运行 每行或返回数据的子查询。
您的问题的最佳解决方案是可更新的 CTE:
WITH toupdate AS
(SELECT S.*,
SUM(SubTotal) OVER (PARTITION BY FiscalYear ORDER BY OrderNumber) AS new_RunningTotal
FROM #Sales S
)
UPDATE toupdate
SET RunningTotal = new_RunningTotal;
这没有联接或相关子查询。通常,window 函数将比相关子查询等效函数快得多。可更新的 CTE 是 SQL 服务器的一项非常好的功能,可以让您免于额外的 JOIN
。