选择最新可用的 SQL 条目状态

发布于 2025-01-17 06:20:45 字数 1041 浏览 0 评论 0原文

考虑这个 DDL:

CREATE TABLE cash_depot_state 
(
    id INTEGER NOT NULL PRIMARY KEY,
    date DATE,
    amount REAL,
    cash_depot_id INTEGER
);
    
INSERT INTO cash_depot_state (date, amount, cash_depot_id) 
VALUES (DATE('2022-03-02'), 382489, 5);
INSERT INTO cash_depot_state (date, amount, cash_depot_id) 
VALUES (DATE('2022-03-03'), 750, 2);
INSERT INTO cash_depot_state (date, amount, cash_depot_id) 
VALUES (DATE('2022-03-04'), 750, 3);
INSERT INTO cash_depot_state (date, amount, cash_depot_id) 
VALUES (DATE('2022-03-05'), 0, 5);

对于一系列日期,我需要选择所有现金仓库实际金额的总和:

  1. 2022-03-01 - 无可用数据 - 预计 0
  2. 2022-03-02 - 现金仓库 #5 已将其值更改为 382489 - 预计 382489
  3. 2022-03-03 - 现金仓库 #2 已将其价值更改为 750 - 预计382489 + 750
  4. 2022-03-03 - 现金仓库 #3 已将其值更改为 750 - 预期 382489 + 750 + 750
  5. 2022-03-04 - 现金仓库 #5 已将其值更改为 0 - 预期 0 + 750 + 750

我的最佳尝试:http://sqlfiddle.com/#!5/94ad0d/1

但我不能弄清楚如何选出子组的获胜者

Consider this DDL:

CREATE TABLE cash_depot_state 
(
    id INTEGER NOT NULL PRIMARY KEY,
    date DATE,
    amount REAL,
    cash_depot_id INTEGER
);
    
INSERT INTO cash_depot_state (date, amount, cash_depot_id) 
VALUES (DATE('2022-03-02'), 382489, 5);
INSERT INTO cash_depot_state (date, amount, cash_depot_id) 
VALUES (DATE('2022-03-03'), 750, 2);
INSERT INTO cash_depot_state (date, amount, cash_depot_id) 
VALUES (DATE('2022-03-04'), 750, 3);
INSERT INTO cash_depot_state (date, amount, cash_depot_id) 
VALUES (DATE('2022-03-05'), 0, 5);

For an array of dates I need to select sum of all cash depots' actual amounts:

  1. 2022-03-01 - no data available - expect 0
  2. 2022-03-02 - cash depot #5 has changed it's value to 382489 - expect 382489
  3. 2022-03-03 - cash depot #2 has changed it's value to 750 - expect 382489 + 750
  4. 2022-03-03 - cash depot #3 has changed it's value to 750 - expect 382489 + 750 + 750
  5. 2022-03-04 - cash depot #5 has changed it's value to 0 - expect 0 + 750 + 750

My best attempt: http://sqlfiddle.com/#!5/94ad0d/1

But I can't figure out how to pick winner of a subgroup

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

↘紸啶 2025-01-24 06:20:45

当您按cash_depot_id划分记录时,您可以将每个现金仓库的最新金额定义为行号为1的记录,并按日期降序排列它们:

SELECT 
    id,
    cash_depot_id,
    date,
    amount,
    ROW_NUMBER() OVER (PARTITION BY cash_depot_id ORDER BY date DESC) rn
FROM
    cash_depot_state

这将突出显示表中的最新数据 - 所有相关行都将包含 rn = 1

idcash_depot_iddateamountrn
222022-03-03750.01
332022-03-04750.01
452022-03-050.01
152022-03-02382489.02

现在您可以使用 WHERE 子句来过滤特定日期的记录,例如 WHERE 数据 <= “2022-03-05”

SELECT
    SUM(amount) sum_amount
FROM
    (
        SELECT amount, ROW_NUMBER() OVER (PARTITION BY cash_depot_id ORDER BY date DESC) rn
        FROM   cash_depot_state 
        WHERE  date <= '2022-03-05'
    ) latest
WHERE
    rn = 1;

将返回 1500


解决此问题的更传统方法是相关子查询:

SELECT
    SUM(amount) sum_amount
FROM
    cash_depot_state s
WHERE
    date = (
        SELECT MAX(date)
        FROM   cash_depot_state
        WHERE  date <= '2022-03-05' AND cash_depot_id = s.cash_depot_id
    )

或针对具体化子查询的联接:

SELECT
    SUM(amount) sum_amount
FROM
    cash_depot_state s
    INNER JOIN (
        SELECT   MAX(date) date, cash_depot_id
        FROM     cash_depot_state
        WHERE    date <= '2022-03-05' 
        GROUP BY cash_depot_id
    ) latest ON latest.cash_depot_id = s.cash_depot_id AND latest.date = s.date

在大型表中,这些可能比 ROW_NUMBER() 变体更快。 YMMV,进行测量。


涵盖 datecash_depot_idamount 的索引可帮助所有显示的方法:

CREATE INDEX ix_latest_cash ON cash_depot_state (date DESC, cash_depot_id ASC, amount);

要针对生成日历的 CTE 运行,可以使用上述任何方法可以作为子查询进行关联,

WITH RECURSIVE dates(date) AS (
  SELECT '2022-03-01'
  UNION ALL
  SELECT date(date, '+1 day') FROM dates WHERE date < DATE('now')
)
SELECT
    date,
    IFNULL(
        (
            -- any of the above approaches with `WHERE date <= dates.date`
        ), 0
    ) balance
FROM
    dates;

例如http://sqlfiddle.com/#!5/94ad0d/12

You could define the latest amount per cash depot as the record that has row number 1, when you divvy up records by cash_depot_id, and order them descending by date:

SELECT 
    id,
    cash_depot_id,
    date,
    amount,
    ROW_NUMBER() OVER (PARTITION BY cash_depot_id ORDER BY date DESC) rn
FROM
    cash_depot_state

This will highlight the latest data from your table - all the relevant rows will have rn = 1:

idcash_depot_iddateamountrn
222022-03-03750.01
332022-03-04750.01
452022-03-050.01
152022-03-02382489.02

Now you can use a WHERE clause to filter records to a certain date, e.g. WHERE data <= '2022-03-05':

SELECT
    SUM(amount) sum_amount
FROM
    (
        SELECT amount, ROW_NUMBER() OVER (PARTITION BY cash_depot_id ORDER BY date DESC) rn
        FROM   cash_depot_state 
        WHERE  date <= '2022-03-05'
    ) latest
WHERE
    rn = 1;

will return 1500.


A more traditional way to solve this would be a correlated sub-query:

SELECT
    SUM(amount) sum_amount
FROM
    cash_depot_state s
WHERE
    date = (
        SELECT MAX(date)
        FROM   cash_depot_state
        WHERE  date <= '2022-03-05' AND cash_depot_id = s.cash_depot_id
    )

or a join against a materialized sub-query:

SELECT
    SUM(amount) sum_amount
FROM
    cash_depot_state s
    INNER JOIN (
        SELECT   MAX(date) date, cash_depot_id
        FROM     cash_depot_state
        WHERE    date <= '2022-03-05' 
        GROUP BY cash_depot_id
    ) latest ON latest.cash_depot_id = s.cash_depot_id AND latest.date = s.date

In large tables, these are potentially faster than the ROW_NUMBER() variant. YMMV, take measurements.


An index that covers date, cash_depot_id, and amount helps all shown approaches:

CREATE INDEX ix_latest_cash ON cash_depot_state (date DESC, cash_depot_id ASC, amount);

To run against a CTE that produces a calendar, any of the above can be correlated as a subquery

WITH RECURSIVE dates(date) AS (
  SELECT '2022-03-01'
  UNION ALL
  SELECT date(date, '+1 day') FROM dates WHERE date < DATE('now')
)
SELECT
    date,
    IFNULL(
        (
            -- any of the above approaches with `WHERE date <= dates.date`
        ), 0
    ) balance
FROM
    dates;

e.g. http://sqlfiddle.com/#!5/94ad0d/12

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文