Принятие последних значений в сумме за диапазон дат - PullRequest
0 голосов
/ 10 октября 2019

У меня есть таблица со следующими столбцами: DeskID *, ProductID *, Date *, Amount (где столбцы, отмеченные *, составляют первичный ключ). Используемые продукты меняются со временем, как показано на рисунке ниже.

Формат таблицы слева и (надеюсь) интуитивное представление данных справа для одного стола

Table format on the left, (hopefully) intuitive representation on the right for one desk

Цель состоит в том, чтобы сумма последних количеств продуктов по датам и датам, включая продукты, которые больше не использовались, за определенный диапазон дат.

например, используя данные над желаемой таблицей:

Desired table

Таким образом, на 1 января сумма равна 1 для продукта A

2-го января сумма равна 2 из A и 5 из B, поэтому 7

4-го января сумма равна 1 из A (не используется, поэтому возьмите значение из 3-го), 5 из B и 2 из C, итого 8

и т. Д.

Я попытался использовать раздел на столе и упорядоченный по дате продукт, чтобы получить самое последнее значение и перевернутьследующий код в функцию (функция 1 ниже) с параметром @date Date

select @date 'Date', t.DeskID, SUM(t.Amount) 'Sum' from (
    select @date 'Date', t.DeskID, t.ProductID, t.Amount
        , row_number() over (partition by t.DeskID, t.ProductID order by t.Date desc) as roworder
    from Table1 t
    where 1 = 1
    and t.Date <= @date
) t
where t.roworder = 1
group by t.DeskID

, а затем с помощью таблицы календаря утилиты иПерекрестное применение для получения требуемых значений во временном диапазоне, как показано ниже:

select * from Calendar c
cross apply Function1(c.CalendarDate)
where c.CalendarDate >= '20190101' and c.CalendarDate <= '20191009'

Это дает ожидаемые результаты, но слишком медленное. В настоящее время на каждом столе используется около 50 продуктов, и каждый месяц эти продукты выпускаются, поэтому всего за 5 лет каждый рабочий стол имеет историю ~ 3000 продуктов, что приводит к остановке всего этого. (Примерно 30 секунд для диапазона одного месяца)

Есть ли лучший подход?

Ответы [ 2 ]

0 голосов
/ 10 октября 2019

Производительность TVF обычно страдает. Следующее полностью удаляет TVF:

-- DROP TABLE Table1;
CREATE TABLE Table1 (DeskID int not null, ProductID nvarchar(32) not null, [Date] Date not null, Amount int not null, PRIMARY KEY ([Date],DeskID,ProductID));

INSERT Table1(DeskID,ProductID,[Date],Amount)
VALUES (1,'A','2019-01-01',1),(1,'A','2019-01-02',2),(1,'B','2019-01-02',5),(1,'A','2019-01-03',1)
,(1,'B','2019-01-03',4),(1,'C','2019-01-03',3),(1,'B','2019-01-04',5),(1,'C','2019-01-04',2),(1,'C','2019-01-05',2)
GO
DECLARE @StartDate date=N'2019-01-01';
DECLARE @EndDate date=N'2019-01-05';
;WITH cte_p
AS
(
    SELECT DISTINCT DeskID,ProductID
    FROM Table1
    WHERE [Date] <= @EndDate
),
cte_a
AS
(
  SELECT @StartDate AS [Date], p.DeskID, p.ProductID, ISNULL(a.Amount,0) AS Amount
  FROM (
    SELECT t.DeskID, t.ProductID
        , MAX(t.Date) AS FirstDate
    FROM Table1 t
    WHERE t.Date <= @StartDate
    GROUP BY t.DeskID, t.ProductID) f
  INNER JOIN  Table1 a
  ON f.DeskID=a.DeskID
  AND f.ProductID=a.ProductID
  AND f.[FirstDate]=a.[Date]
  RIGHT JOIN cte_p p
  ON p.DeskID=a.DeskID
  AND p.ProductID=a.ProductID
  UNION ALL
  SELECT  DATEADD(DAY,1,a.[Date]) AS [Date], t.DeskID, t.ProductID, t.Amount
  FROM Table1 t
  INNER JOIN cte_a a
  ON t.DeskID=a.DeskID
  AND t.ProductID=a.ProductID
  AND t.[Date] > a.[Date]
  AND t.[Date] <= DATEADD(DAY,1,a.[Date])
  WHERE a.[Date]<@EndDate
  UNION ALL
  SELECT DATEADD(DAY,1,a.[Date]) AS [Date], a.DeskID, a.ProductID, a.Amount
  FROM cte_a a
  WHERE NOT EXISTS(SELECT 1 FROM Table1 t
      WHERE t.DeskID=a.DeskID
      AND t.ProductID=a.ProductID
      AND t.[Date] > a.[Date]
      AND t.[Date] <= DATEADD(DAY,1,a.[Date]))
  AND a.[Date]<@EndDate
 )
 SELECT [Date], DeskID, SUM(Amount)
 FROM cte_a
 GROUP BY [Date], DeskID;
0 голосов
/ 10 октября 2019

Изменение вашей функции должно быть быстрее:

select @date 'Date', t.DeskID, SUM(t.Amount) 'Sum'
FROM (SELECT m.DeskID, m.ProductID, MAX(m.[Date) AS MaxDate
  FROM Table1 m
  where m.[Date] <= @date) d
INNER JOIN Table1 t
ON d.DeskID=t.DeskID
AND d.ProductID=t.ProductID
and t.[Date] = d.MaxDate
group by t.DeskID
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...