Выполнение общего запроса SQL Server
Пока у меня есть следующий запрос SQL Server 2005:
WITH D AS (
SELECT CONVERT(VARCHAR, '2020.11.01', 102) AS d_y_m, CAST('2020-11-01' AS DATETIME) AS dt
UNION ALL
SELECT CONVERT(VARCHAR, DATEADD(dd, 1, z.dt), 102) AS d_y_m, DATEADD(dd, 1, z.dt)
FROM D AS z
WHERE DATEADD(dd, 1, z.dt) <= '2020-11-30')
SELECT x.d_y_m, ISNULL(SUM(y.Total), 0) AS [Invoiced], ISNULL(SUM(FEI.Total), 0) AS [Paid] FROM D x
LEFT JOIN Invoices y ON CONVERT(VARCHAR, y.InvoiceDate, 102) = x.d_y_m
LEFT JOIN Payments AS FEI ON CONVERT(VARCHAR, FEI.PaymentDate, 102) = x.d_y_m
GROUP BY x.d_y_m
ORDER BY x.d_y_m OPTION (MAXRECURSION 0)
Как мне добавить еще один столбец (RunningTotal) в запрос, который суммирует результат (Invoiced-Paid) за предыдущий день с результатом на сегодняшний день
Пример:
d_y_m | Invoiced | Paid | RunningTotal
2020.11.01 | 24 | 5 | 19
2020.11.02 | 45 | 2 | 62
2020.11.03 | 10 | 20 | 52
2020.11.04 | 5 | 0 | 57
2020.11.05 | 0 | 10 | 47
1 ответ
Пара замечаний по вашему текущему решению:
- Не используйте "случайные" псевдонимы таблиц.
D
для "дат" имеет смысл.y
для "Счетов" нет.d_y_m
также не соответствует вашему формату даты. Сохраняйте значимость псевдонимов таблиц и столбцов. - Не перетаскивайте преобразование даты через все решение. Работайте со значениями даты как
date
типы и конвертировать значения один раз в финальномselect
. - Не группируйте суммы выставленных и оплаченных сумм в одном запросе. Если у вас есть несколько счетов или платежей в один день, то суммы будут неверными! Объяснение см. в разделе «Дополнительно» внизу.
- Сделайте так, чтобы нам было легко вам помочь. В следующий раз, пожалуйста, предоставьте образцы данных, которые мы можем скопировать и вставить вместо того, чтобы изобретать свои собственные.
- SQL Server 2005 официально не поддерживается с 12 апреля 2016 г. Пришло время искать новую версию!
Образец данных
create table Invoices
(
InvoiceDate date,
Total money
);
insert into Invoices (InvoiceDate, Total) values
('2020-11-01', 20),
('2020-11-01', 4),
('2020-11-02', 40),
('2020-11-02', 5),
('2020-11-03', 10),
('2020-11-04', 3),
('2020-11-04', 2);
create table Payments
(
PaymentDate date,
Total money
);
insert into Payments (PaymentDate, Total) values
('2020-11-01', 5),
('2020-11-02', 2),
('2020-11-03', 10),
('2020-11-03', 10),
('2020-11-05', 10);
Решение
with DateRange as
(
select convert(date, '2020-11-01') as DateValue
union all
select dateadd(day, 1, dr.DateValue)
from DateRange dr
where dr.DateValue < '2020-11-30'
),
InvoicedTotal as
(
select dr.DateValue,
isnull(sum(i.Total), 0) as Invoiced
from DateRange dr
left join Invoices i
on i.InvoiceDate = dr.DateValue
group by dr.DateValue
),
PaidTotal as
(
select dr.DateValue,
isnull(sum(p.Total), 0) as Paid
from DateRange dr
left join Payments p
on p.PaymentDate = dr.DateValue
group by dr.DateValue
)
select convert(varchar(10), dr.DateValue, 102) as [YYYY.MM.DD],
it.Invoiced as [Invoiced],
sum(it.Invoiced) over(order by it.DateValue
rows between unbounded preceding and current row) as [CumInvoiced],
pt.Paid as [Paid],
sum(pt.Paid) over(order by pt.DateValue
rows between unbounded preceding and current row) as [CumPaid],
sum(it.Invoiced) over(order by it.DateValue
rows between unbounded preceding and current row) -
sum(pt.Paid) over(order by pt.DateValue
rows between unbounded preceding and current row) as [RunningTotal]
from DateRange dr
join InvoicedTotal it
on it.DateValue = dr.DateValue
join PaidTotal pt
on pt.DateValue = dr.DateValue
order by dr.DateValue;
Результат
Перечислены только первые 10 из 30 строк за ноябрь.
YYYY.MM.DD Invoiced CumInvoiced Paid CumPaid RunningTotal
---------- -------- ----------- ------- ------- ------------
2020.11.01 24.0000 24.0000 5.0000 5.0000 19.0000
2020.11.02 45.0000 69.0000 2.0000 7.0000 62.0000
2020.11.03 10.0000 79.0000 20.0000 27.0000 52.0000
2020.11.04 5.0000 84.0000 0.0000 27.0000 57.0000
2020.11.05 0.0000 84.0000 10.0000 37.0000 47.0000
2020.11.06 0.0000 84.0000 0.0000 37.0000 47.0000
2020.11.07 0.0000 84.0000 0.0000 37.0000 47.0000
2020.11.08 0.0000 84.0000 0.0000 37.0000 47.0000
2020.11.09 0.0000 84.0000 0.0000 37.0000 47.0000
2020.11.10 0.0000 84.0000 0.0000 37.0000 47.0000
Скрипка , чтобы увидеть его в действии.
Дополнительно: почему бы не подсчитать обе суммы в одном запросе.
Используя те же данные образца, вы можете запустить этот запрос, чтобы увеличить конкретную дату, здесь:
2020-11-01
. На эту дату данные выборки содержат 2 счета и 1 платеж.
with DateRange as
(
select '2020-11-01' as DateValue -- filtering data to explain
)
select dr.DateValue,
isnull(sum(i.Total), 0) as Invoiced,
isnull(sum(p.Total), 0) as Paid
from DateRange dr
left join Invoices i
on i.InvoiceDate = dr.DateValue
left join Payments p
on p.PaymentDate = dr.DateValue
group by dr.DateValue
order by dr.DateValue;
Просто выполнение соединений даст вам результат ниже. Из-за комбинированного
left join
строка оплаты указана дважды!
dr.DateValue | i.Total | p.Total
------------ | ------- | -------
2020-11-01 | 20 | 5
2020-11-01 | 4 | 5 --> payment row got joined TWICE
Суммирование этих строк дает неверную сумму платежа за этот день.
group by dr.DateValue | sum(i.Total) | sum(p.Total)
--------------------- | ------------ | ------------
2020-11-01 | 24 | 10 --> last sum is WRONG !
Изменить: версия SQL Server 2005 с
cross apply
. Но обновление версии SQL Server по-прежнему рекомендуется!
with DateRange as
(
select convert(date, '2020-11-01') as DateValue
union all
select dateadd(day, 1, dr.DateValue)
from DateRange dr
where dr.DateValue < '2020-11-30'
),
InvoicedTotal as
(
select dr.DateValue,
isnull(sum(i.Total), 0) as Invoiced
from DateRange dr
left join Invoices i
on i.InvoiceDate = dr.DateValue
group by dr.DateValue
),
PaidTotal as
(
select dr.DateValue,
isnull(sum(p.Total), 0) as Paid
from DateRange dr
left join Payments p
on p.PaymentDate = dr.DateValue
group by dr.DateValue
)
select convert(varchar(10), dr.DateValue, 102) as [YYYY.MM.DD],
it1.Invoiced as [Invoiced],
it3.Invoiced as [CumInvoiced],
pt1.Paid as [Paid],
pt3.Paid as [CumPaid],
it3.Invoiced - pt3.Paid as [RunningTotal]
from DateRange dr
join InvoicedTotal it1
on it1.DateValue = dr.DateValue
join PaidTotal pt1
on pt1.DateValue = dr.DateValue
cross apply ( select sum(it2.Invoiced) as Invoiced
from InvoicedTotal it2
where it2.DateValue <= dr.DateValue ) it3
cross apply ( select sum(pt2.Paid) as Paid
from PaidTotal pt2
where pt2.DateValue <= dr.DateValue ) pt3
order by dr.DateValue;