Где пункт на Итоговый итог - PullRequest
1 голос
/ 22 марта 2019

У меня есть эта таблица, в которой хранятся контейнеры по регионам и количеству кофейных мешочков в каждом контейнере.

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 кофейных пакетиков.Переполнение - это нормально.

Вот запрос, который я сформулировал

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. Специально таблица «Контейнер» - это очень большая таблица, и я думаю, что подзапрос sq1 излишне вычислит значения RunningTotals для всех контейнеров в регионе.Мне было интересно, есть ли в любом случае sq1 перестать обрабатывать больше строк, как только RunnningTotal превысит @ RequiredCoffeePouches.

1 Ответ

0 голосов
/ 22 марта 2019

Две вещи:

Перемещение вашего предложения 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

Если вы используете SSMS, выберите «Включить статистику клиента» и «Включить фактический план выполнения», чтобы отслеживать, как выполняется ваш запрос при его создании.

...