【问题标题】:Where clause on Running total运行总计的 Where 子句
【发布时间】:2019-08-13 14:16:21
【问题描述】:

我有这张表,它按地区存储容器以及每个容器中咖啡袋的数量。

if object_id( 'dbo.Container' ) is not null
    drop table dbo.Container
go

create table dbo.Container
(
    Id   int not null,
    Region int not null,
    NumberOfCoffeePouches int not null,
    constraint pkc_Container__Id primary key clustered(Id asc)
)
go

insert into dbo.Container 
    ( Id , Region , NumberOfCoffeePouches ) 
values 
    ( 1, 1, 10 ),
    ( 2, 1, 30 ),
    ( 3, 1, 5),
    ( 4, 1, 7),
    ( 5, 1, 1),
    ( 6, 1, 3),
    ( 7, 2, 4),
    ( 8, 2, 4),
    ( 9, 2, 4)

我需要列出将用于完成订单(例如 50 个咖啡袋)的容器 ID。供应过剩是可以的。

这是我提出的问题

declare @RequiredCoffeePouches int = 50

select
        sq2.Id,
        sq2.NumberOfCoffeePouches,
        sq2.RunningTotal,
        sq2.LagRunningTotal
from
    (
        select
            sq1.Id,
            sq1.NumberOfCoffeePouches,
            sq1.RunningTotal,
            lag(sq1.RunningTotal, 1, 0) over (order by sq1.Id asc) 
                as 'LagRunningTotal'
        from
            (
                select
                    c.Id,
                    c.NumberOfCoffeePouches,
                    sum(c.NumberOfCoffeePouches) 
                        over (order by c.Id asc) as 'RunningTotal'
                from
                    dbo.Container as c
                where
                    c.Region = 1
            ) as sq1
    ) as sq2
where
    sq2.LagRunningTotal <= @RequiredCoffeePouches

它给出了预期的结果

Id          NumberOfCoffeePouches RunningTotal LagRunningTotal
----------- --------------------- ------------ ---------------
1           10                    10           0
2           30                    40           10
3           5                     45           40
4           7                     52           45

问题:

  1. 是否有更好、更优化的方法来实现这一目标?
  2. 特别是 Container 表是非常大的表,我认为子查询 sq1 将不必要地计算该区域中所有容器的 RunningTotals。我想知道一旦 RunnningTotal 超过 @RequiredCoffeePouches 是否有任何方法让 sq1 停止处理更多行。

【问题讨论】:

  • 一般来说停止是不安全的。运行总数可以以负值下降和上升。但即使它们被限制为积极的 SQL Server 仍然不会为您执行此操作。您可以在这里运行两次查询stackoverflow.com/a/53971884/73226

标签: sql sql-server tsql sql-server-2014


【解决方案1】:

两件事:

WHERE 子句移动到相关子选择中可以大大提高查询速度,因为它会提取更少的数据。使用您的示例:

SELECT
    sq2.Id,
    sq2.NumberOfCoffeePouches,
    sq2.RunningTotal,
    sq2.LagRunningTotal
FROM
    (
    SELECT
        sq1.Id,
        sq1.NumberOfCoffeePouches,
        sq1.RunningTotal,
        lag(sq1.RunningTotal, 1, 0) over (order by sq1.Id asc) AS 'LagRunningTotal'
    FROM
        (
         SELECT
             c.Id,
             c.NumberOfCoffeePouches,
             SUM(c.NumberOfCoffeePouches) OVER (order by c.Id asc) AS 'RunningTotal'
         FROM dbo.Container AS c
         WHERE c.Region = 1
        ) AS sq1
    WHERE sq2.LagRunningTotal <= @RequiredCoffeePouches
) AS sq2

CTE 还可以提高性能:

;WITH sql1CTE AS (
    SELECT
        c.Id,
        c.NumberOfCoffeePouches,
        SUM(c.NumberOfCoffeePouches) OVER (order by c.Id asc) AS 'RunningTotal'
    FROM dbo.Container AS c
    WHERE c.Region = 1
),
sql2CTE AS (
SELECT
        Id,
        NumberOfCoffeePouches,
        RunningTotal,
        lag(RunningTotal, 1, 0) over (order by Id asc) AS 'LagRunningTotal'
FROM sql1CTE
WHERE LagRunningTotal <= @RequiredCoffeePouches
)
SELECT
    Id,
    NumberOfCoffeePouches,
    RunningTotal,
    LagRunningTotal
FROM sql2CTE

SQL Server CTE Basics

如果您使用的是 SSMS,请选择“包括客户统计信息”和“包括实际执行计划”以跟踪查询在您设计时的执行情况。

【讨论】:

  • CTE 与派生表相比没有性能优势。在安全的情况下,SQL Server 通常可以将谓词下推到派生表中。
  • 在这种情况下,谓词必须保持原样,因为它引用了不在您放置它的WHERE 范围内的别名
猜你喜欢
  • 2017-07-14
  • 1970-01-01
  • 1970-01-01
  • 2019-02-04
  • 1970-01-01
  • 2014-10-12
  • 2012-12-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多