SQL 按日期查找条目总和,包括上一个日期
SQL find sum of entries by date including previous date
这可能是不可能的,但这是我试图翻译成 SQL 的请求:
对于 ACCOUNT_ENTRY
table 中至少存在一个条目的每个日期,我想找到所有帐户(在 ACCOUNT
table 中)的所有条目的总和日期。如果某个帐户没有该特定日期的条目,我想使用截至该日期的下一个最新条目。
请注意,我目前正在 H2 数据库上执行此操作,但将来可能会发生变化,因此我试图远离供应商特定的存储过程。
例子
ACCOUNT
table
ID
ACCOUNT_LABEL
1
账户 123
2
帐号 456
3
账号789
ACCOUNT_ENTRY
table
ID
ACCOUNT_ID
ENTRY_DATE
BOOK_VALUE
MARKET_VALUE
7
1
2021-05-31
100
110
5
1
2021-05-28
90
100
6
2
2021-05-28
70
80
4
3
2021-05-28
50
60
3
1
2021-05-27
80
90
2
2
2021-05-27
60
70
1
3
2021-05-27
40
50
想要的结果
ENTRY_DATE
SUM_OF_BOOK_VAL
SUM_OF_MARKET_VAL
2021-05-27
180
210
2021-05-28
210
240
2021-05-31
220
250
2021-05-27
账面值=80+60+40(行号1+2+3)
2021-05-27
市值=90+70+50(行号1+2+3)
2021-05-28
账面值=90+70+50(行号4+5+6)
2021-05-28
市值=100+80+60(行号4+5+6)
2021-05-31
等于 2021-05-28
的结果 + 来自 2021-05-31
的新单个新条目(行 ID = 7)
- 帐面价值 = 70 + 50 + 100 = 220
- 市值=80+60+110=250
到目前为止我能得到的结果
ENTRY_DATE
SUM_OF_BOOK_VAL
SUM_OF_MARKET_VAL
2021-05-27
180
210
2021-05-28
210
240
2021-05-31
100
110
这在我的应用程序代码中很容易做到,但我希望将计算留给数据库而不是将所有数据传输到应用程序。我也可以前滚数据,但最终可能会得到大量不必要的数据。
这是我目前使用的查询,但它不处理必须回顾以前条目的情况。
查询
SELECT
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd') AS ENTRYDATE,
SUM(
CASE
WHEN A.JOINT_ACCOUNT = 'TRUE' THEN MARKET_VALUE / 2
ELSE MARKET_VALUE
END
) AS MARKETVALUE,
SUM(
CASE
WHEN A.JOINT_ACCOUNT = 'TRUE' THEN BOOK_VALUE / 2
ELSE BOOK_VALUE
END
) AS BOOKVALUE
FROM
ACCOUNT_ENTRY AE
INNER JOIN ACCOUNT A ON AE.ACCOUNT_ID = A.ID
INNER JOIN (
SELECT
MAX(ID) AS MAX_ID,
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd'),
AE.ACCOUNT_ID AS AID
FROM
ACCOUNT_ENTRY AE
GROUP BY
AE.ACCOUNT_ID,
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd')
) MAX_ENTRIES ON AE.ID = MAX_ENTRIES.MAX_ID
GROUP BY
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd')
ORDER BY
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd') DESC
这可能是不可能的,但这是我试图翻译成 SQL 的请求:
对于 ACCOUNT_ENTRY
table 中至少存在一个条目的每个日期,我想找到所有帐户(在 ACCOUNT
table 中)的所有条目的总和日期。如果某个帐户没有该特定日期的条目,我想使用截至该日期的下一个最新条目。
请注意,我目前正在 H2 数据库上执行此操作,但将来可能会发生变化,因此我试图远离供应商特定的存储过程。
例子
ACCOUNT
table
ID
ACCOUNT_LABEL
1
账户 123
2
帐号 456
3
账号789
ACCOUNT_ENTRY
table
ID
ACCOUNT_ID
ENTRY_DATE
BOOK_VALUE
MARKET_VALUE
7
1
2021-05-31
100
110
5
1
2021-05-28
90
100
6
2
2021-05-28
70
80
4
3
2021-05-28
50
60
3
1
2021-05-27
80
90
2
2
2021-05-27
60
70
1
3
2021-05-27
40
50
想要的结果
ENTRY_DATE
SUM_OF_BOOK_VAL
SUM_OF_MARKET_VAL
2021-05-27
180
210
2021-05-28
210
240
2021-05-31
220
250
2021-05-27
账面值=80+60+40(行号1+2+3)
2021-05-27
市值=90+70+50(行号1+2+3)
2021-05-28
账面值=90+70+50(行号4+5+6)
2021-05-28
市值=100+80+60(行号4+5+6)
2021-05-31
等于 2021-05-28
的结果 + 来自 2021-05-31
的新单个新条目(行 ID = 7)
- 帐面价值 = 70 + 50 + 100 = 220
- 市值=80+60+110=250
到目前为止我能得到的结果
ENTRY_DATE
SUM_OF_BOOK_VAL
SUM_OF_MARKET_VAL
2021-05-27
180
210
2021-05-28
210
240
2021-05-31
100
110
这在我的应用程序代码中很容易做到,但我希望将计算留给数据库而不是将所有数据传输到应用程序。我也可以前滚数据,但最终可能会得到大量不必要的数据。
这是我目前使用的查询,但它不处理必须回顾以前条目的情况。
查询
SELECT
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd') AS ENTRYDATE,
SUM(
CASE
WHEN A.JOINT_ACCOUNT = 'TRUE' THEN MARKET_VALUE / 2
ELSE MARKET_VALUE
END
) AS MARKETVALUE,
SUM(
CASE
WHEN A.JOINT_ACCOUNT = 'TRUE' THEN BOOK_VALUE / 2
ELSE BOOK_VALUE
END
) AS BOOKVALUE
FROM
ACCOUNT_ENTRY AE
INNER JOIN ACCOUNT A ON AE.ACCOUNT_ID = A.ID
INNER JOIN (
SELECT
MAX(ID) AS MAX_ID,
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd'),
AE.ACCOUNT_ID AS AID
FROM
ACCOUNT_ENTRY AE
GROUP BY
AE.ACCOUNT_ID,
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd')
) MAX_ENTRIES ON AE.ID = MAX_ENTRIES.MAX_ID
GROUP BY
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd')
ORDER BY
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd') DESC
找到解决方案
感谢@Gordon Linoff 分享 COALESCE 和 LAG 函数。
首先,为了简化我的查询,我创建了一个视图。这与我面临的挑战无关,但很重要,因此我可以消除同一帐户和同一天的多个条目
create view LATEST_ACCOUNT_ENTRY as
select
max(id) as entry_id,
account_id,
to_char(entry_date, 'yyyy-mm-dd') as e_date
from
account_entry
group by
account_id,
e_date
最终查询(将被简化)
select
e_date,
sum(coalesce(BOOK_VALUE, PREVIOUS_BOOK_VALUE)),
sum(coalesce(MARKET_VALUE, PREVIOUS_MARKET_VALUE))
from
(
select
dates.e_date,
acc_id,
entry_id,
book_value,
LAG(book_value) OVER (
partition by dates.acc_id
order by
dates.e_date
) as previous_book_value,
market_value,
LAG(market_value) OVER (
partition by dates.acc_id
order by
dates.e_date
) as previous_market_value
from
(
select
distinct to_char(entry_date, 'yyyy-mm-dd') as e_date,
accts.id as acc_id
from
account_entry
cross join (
select
id
from
account
) accts
) dates
left join (
select
lae.ACCOUNT_ID,
lae.E_DATE,
lae.ENTRY_ID,
CASE
WHEN JOINT_ACCOUNT = 'TRUE' THEN BOOK_VALUE / 2
ELSE BOOK_VALUE
END as BOOK_VALUE,
CASE
WHEN JOINT_ACCOUNT = 'TRUE' THEN MARKET_VALUE / 2
ELSE MARKET_VALUE
END as MARKET_VALUE
from
LATEST_ACCOUNT_ENTRY lae
inner join account_entry ae on lae.ENTRY_ID = ae.id
inner join account acc on ae.account_id = acc.id
) entries on dates.e_date = entries.e_date
and dates.acc_id = entries.account_id
)
group by
e_date
您可以通过为所有日期和帐户生成行然后使用 lag(ignore nulls)
获取最新值来完成此操作。所以,对于行:
select a.account_id, d.entry_date, ae.book_val, ae.market_val,
coalesce(ae.book_val,
lag(ae.book_val) ignore nulls over (partition by a.account_id order by d.entry_date)
) as imputed_book_val,
coalesce(ae.market_val,
lag(ae.market_val) ignore nulls over (partition by a.account_id order by d.entry_date)
) as imputed_market_val
from (select distinct account_id from account_entry) a cross join
(select distinct entry_date from account_entry) d left join
account_entry ae
on ae.account_id = a.account_id and
ae.entry_date = d.entry_date;
然后你可以汇总这个:
select entry_date, sum(imputed_book_val), sum(imputed_market_val)
from (select a.account_id, d.entry_date, ae.book_val, ae.market_val,
coalesce(ae.book_val,
lag(ae.book_val) ignore nulls over (partition by a.account_id order by d.entry_date)
) as imputed_book_val,
coalesce(ae.market_val,
lag(ae.market_val) ignore nulls over (partition by a.account_id order by d.entry_date)
) as imputed_market_val
from (select distinct account_id from account_entry) a cross join
(select distinct entry_date from account_entry) d left join
account_entry ae
on ae.account_id = a.account_id and
ae.entry_date = d.entry_date
) ad
group by entry_date;
这可能是不可能的,但这是我试图翻译成 SQL 的请求:
对于 ACCOUNT_ENTRY
table 中至少存在一个条目的每个日期,我想找到所有帐户(在 ACCOUNT
table 中)的所有条目的总和日期。如果某个帐户没有该特定日期的条目,我想使用截至该日期的下一个最新条目。
请注意,我目前正在 H2 数据库上执行此操作,但将来可能会发生变化,因此我试图远离供应商特定的存储过程。
例子
ACCOUNT
table
ID | ACCOUNT_LABEL |
---|---|
1 | 账户 123 |
2 | 帐号 456 |
3 | 账号789 |
ACCOUNT_ENTRY
table
ID | ACCOUNT_ID | ENTRY_DATE | BOOK_VALUE | MARKET_VALUE |
---|---|---|---|---|
7 | 1 | 2021-05-31 | 100 | 110 |
5 | 1 | 2021-05-28 | 90 | 100 |
6 | 2 | 2021-05-28 | 70 | 80 |
4 | 3 | 2021-05-28 | 50 | 60 |
3 | 1 | 2021-05-27 | 80 | 90 |
2 | 2 | 2021-05-27 | 60 | 70 |
1 | 3 | 2021-05-27 | 40 | 50 |
想要的结果
ENTRY_DATE | SUM_OF_BOOK_VAL | SUM_OF_MARKET_VAL |
---|---|---|
2021-05-27 | 180 | 210 |
2021-05-28 | 210 | 240 |
2021-05-31 | 220 | 250 |
2021-05-27
账面值=80+60+40(行号1+2+3)2021-05-27
市值=90+70+50(行号1+2+3)2021-05-28
账面值=90+70+50(行号4+5+6)2021-05-28
市值=100+80+60(行号4+5+6)2021-05-31
等于2021-05-28
的结果 + 来自2021-05-31
的新单个新条目(行 ID = 7)- 帐面价值 = 70 + 50 + 100 = 220
- 市值=80+60+110=250
到目前为止我能得到的结果
ENTRY_DATE | SUM_OF_BOOK_VAL | SUM_OF_MARKET_VAL |
---|---|---|
2021-05-27 | 180 | 210 |
2021-05-28 | 210 | 240 |
2021-05-31 | 100 | 110 |
这在我的应用程序代码中很容易做到,但我希望将计算留给数据库而不是将所有数据传输到应用程序。我也可以前滚数据,但最终可能会得到大量不必要的数据。
这是我目前使用的查询,但它不处理必须回顾以前条目的情况。
查询
SELECT
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd') AS ENTRYDATE,
SUM(
CASE
WHEN A.JOINT_ACCOUNT = 'TRUE' THEN MARKET_VALUE / 2
ELSE MARKET_VALUE
END
) AS MARKETVALUE,
SUM(
CASE
WHEN A.JOINT_ACCOUNT = 'TRUE' THEN BOOK_VALUE / 2
ELSE BOOK_VALUE
END
) AS BOOKVALUE
FROM
ACCOUNT_ENTRY AE
INNER JOIN ACCOUNT A ON AE.ACCOUNT_ID = A.ID
INNER JOIN (
SELECT
MAX(ID) AS MAX_ID,
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd'),
AE.ACCOUNT_ID AS AID
FROM
ACCOUNT_ENTRY AE
GROUP BY
AE.ACCOUNT_ID,
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd')
) MAX_ENTRIES ON AE.ID = MAX_ENTRIES.MAX_ID
GROUP BY
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd')
ORDER BY
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd') DESC
这可能是不可能的,但这是我试图翻译成 SQL 的请求:
对于 ACCOUNT_ENTRY
table 中至少存在一个条目的每个日期,我想找到所有帐户(在 ACCOUNT
table 中)的所有条目的总和日期。如果某个帐户没有该特定日期的条目,我想使用截至该日期的下一个最新条目。
请注意,我目前正在 H2 数据库上执行此操作,但将来可能会发生变化,因此我试图远离供应商特定的存储过程。
例子
ACCOUNT
table
ID | ACCOUNT_LABEL |
---|---|
1 | 账户 123 |
2 | 帐号 456 |
3 | 账号789 |
ACCOUNT_ENTRY
table
ID | ACCOUNT_ID | ENTRY_DATE | BOOK_VALUE | MARKET_VALUE |
---|---|---|---|---|
7 | 1 | 2021-05-31 | 100 | 110 |
5 | 1 | 2021-05-28 | 90 | 100 |
6 | 2 | 2021-05-28 | 70 | 80 |
4 | 3 | 2021-05-28 | 50 | 60 |
3 | 1 | 2021-05-27 | 80 | 90 |
2 | 2 | 2021-05-27 | 60 | 70 |
1 | 3 | 2021-05-27 | 40 | 50 |
想要的结果
ENTRY_DATE | SUM_OF_BOOK_VAL | SUM_OF_MARKET_VAL |
---|---|---|
2021-05-27 | 180 | 210 |
2021-05-28 | 210 | 240 |
2021-05-31 | 220 | 250 |
2021-05-27
账面值=80+60+40(行号1+2+3)2021-05-27
市值=90+70+50(行号1+2+3)2021-05-28
账面值=90+70+50(行号4+5+6)2021-05-28
市值=100+80+60(行号4+5+6)2021-05-31
等于2021-05-28
的结果 + 来自2021-05-31
的新单个新条目(行 ID = 7)- 帐面价值 = 70 + 50 + 100 = 220
- 市值=80+60+110=250
到目前为止我能得到的结果
ENTRY_DATE | SUM_OF_BOOK_VAL | SUM_OF_MARKET_VAL |
---|---|---|
2021-05-27 | 180 | 210 |
2021-05-28 | 210 | 240 |
2021-05-31 | 100 | 110 |
这在我的应用程序代码中很容易做到,但我希望将计算留给数据库而不是将所有数据传输到应用程序。我也可以前滚数据,但最终可能会得到大量不必要的数据。
这是我目前使用的查询,但它不处理必须回顾以前条目的情况。
查询
SELECT
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd') AS ENTRYDATE,
SUM(
CASE
WHEN A.JOINT_ACCOUNT = 'TRUE' THEN MARKET_VALUE / 2
ELSE MARKET_VALUE
END
) AS MARKETVALUE,
SUM(
CASE
WHEN A.JOINT_ACCOUNT = 'TRUE' THEN BOOK_VALUE / 2
ELSE BOOK_VALUE
END
) AS BOOKVALUE
FROM
ACCOUNT_ENTRY AE
INNER JOIN ACCOUNT A ON AE.ACCOUNT_ID = A.ID
INNER JOIN (
SELECT
MAX(ID) AS MAX_ID,
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd'),
AE.ACCOUNT_ID AS AID
FROM
ACCOUNT_ENTRY AE
GROUP BY
AE.ACCOUNT_ID,
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd')
) MAX_ENTRIES ON AE.ID = MAX_ENTRIES.MAX_ID
GROUP BY
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd')
ORDER BY
TO_CHAR(ENTRY_DATE, 'yyyy-mm-dd') DESC
找到解决方案
感谢@Gordon Linoff 分享 COALESCE 和 LAG 函数。
首先,为了简化我的查询,我创建了一个视图。这与我面临的挑战无关,但很重要,因此我可以消除同一帐户和同一天的多个条目
create view LATEST_ACCOUNT_ENTRY as
select
max(id) as entry_id,
account_id,
to_char(entry_date, 'yyyy-mm-dd') as e_date
from
account_entry
group by
account_id,
e_date
最终查询(将被简化)
select
e_date,
sum(coalesce(BOOK_VALUE, PREVIOUS_BOOK_VALUE)),
sum(coalesce(MARKET_VALUE, PREVIOUS_MARKET_VALUE))
from
(
select
dates.e_date,
acc_id,
entry_id,
book_value,
LAG(book_value) OVER (
partition by dates.acc_id
order by
dates.e_date
) as previous_book_value,
market_value,
LAG(market_value) OVER (
partition by dates.acc_id
order by
dates.e_date
) as previous_market_value
from
(
select
distinct to_char(entry_date, 'yyyy-mm-dd') as e_date,
accts.id as acc_id
from
account_entry
cross join (
select
id
from
account
) accts
) dates
left join (
select
lae.ACCOUNT_ID,
lae.E_DATE,
lae.ENTRY_ID,
CASE
WHEN JOINT_ACCOUNT = 'TRUE' THEN BOOK_VALUE / 2
ELSE BOOK_VALUE
END as BOOK_VALUE,
CASE
WHEN JOINT_ACCOUNT = 'TRUE' THEN MARKET_VALUE / 2
ELSE MARKET_VALUE
END as MARKET_VALUE
from
LATEST_ACCOUNT_ENTRY lae
inner join account_entry ae on lae.ENTRY_ID = ae.id
inner join account acc on ae.account_id = acc.id
) entries on dates.e_date = entries.e_date
and dates.acc_id = entries.account_id
)
group by
e_date
您可以通过为所有日期和帐户生成行然后使用 lag(ignore nulls)
获取最新值来完成此操作。所以,对于行:
select a.account_id, d.entry_date, ae.book_val, ae.market_val,
coalesce(ae.book_val,
lag(ae.book_val) ignore nulls over (partition by a.account_id order by d.entry_date)
) as imputed_book_val,
coalesce(ae.market_val,
lag(ae.market_val) ignore nulls over (partition by a.account_id order by d.entry_date)
) as imputed_market_val
from (select distinct account_id from account_entry) a cross join
(select distinct entry_date from account_entry) d left join
account_entry ae
on ae.account_id = a.account_id and
ae.entry_date = d.entry_date;
然后你可以汇总这个:
select entry_date, sum(imputed_book_val), sum(imputed_market_val)
from (select a.account_id, d.entry_date, ae.book_val, ae.market_val,
coalesce(ae.book_val,
lag(ae.book_val) ignore nulls over (partition by a.account_id order by d.entry_date)
) as imputed_book_val,
coalesce(ae.market_val,
lag(ae.market_val) ignore nulls over (partition by a.account_id order by d.entry_date)
) as imputed_market_val
from (select distinct account_id from account_entry) a cross join
(select distinct entry_date from account_entry) d left join
account_entry ae
on ae.account_id = a.account_id and
ae.entry_date = d.entry_date
) ad
group by entry_date;