【问题标题】:How to rewrite SQL joins into window functions?如何将 SQL 连接重写为窗口函数?
【发布时间】:2017-12-10 21:21:40
【问题描述】:

数据库是 HP Vertica 7 或 PostgreSQL 9。

create table test (
id int,
card_id int,
tran_dt date,
amount int
);

insert into test values (1, 1, '2017-07-06', 10);
insert into test values (2, 1, '2017-06-01', 20);
insert into test values (3, 1, '2017-05-01', 30);
insert into test values (4, 1, '2017-04-01', 40);
insert into test values (5, 2, '2017-07-04', 10);

在过去 1 天使用的支付卡中,该卡在过去 90 天内收取的最高金额是多少。

select t.card_id, max(t2.amount) max
from test t
join test t2 on t2.card_id=t.card_id and t2.tran_dt>='2017-04-06'
where t.tran_dt>='2017-07-06'
group by t.card_id
order by t.card_id;

结果正确

card_id    max
-------    ---
1          30

我想将查询重写为 sql 窗口函数。

select card_id, max(amount) over(partition by card_id order by tran_dt range between '60 days' preceding and current row) max
from test
where card_id in (select card_id from test where tran_dt>='2017-07-06')
order by card_id;

但是结果集不匹配,怎么办?

这里的测试数据: http://sqlfiddle.com/#!17/db317/1

【问题讨论】:

  • 首先,Postgres 和 Vertica 是两个截然不同的东西。其次,为什么您期望两个非常不同的查询的结果是相同的?

标签: postgresql join window-functions vertica


【解决方案1】:

我不能尝试 PostgreSQL,但在 Vertica 中,您可以应用 ANSI 标准 OLAP 窗口函数。

但是您需要嵌套两个查询: window 函数只有在结果集中包含需要评估的所有行时才返回合理的结果。

但您只想显示“2017-07-06”中的行。

因此,您必须在外部查询中过滤该日期:

WITH olap_output AS (
  SELECT 
    card_id
  , tran_dt
  , MAX(amount) OVER (
      PARTITION BY card_id
      ORDER BY tran_dt
      RANGE BETWEEN '90 DAYS' PRECEDING AND CURRENT ROW
    ) AS the_max
  FROM test
)
SELECT
  card_id
, the_max
FROM olap_output
WHERE tran_dt='2017-07-06'
;

card_id|the_max
      1|     30

【讨论】:

    【解决方案2】:

    据我所知,PostgreSQL Window 函数不支持有界range preceding,因此range between '90 days' preceding 不起作用。它确实支持有界rows preceding,例如rows between 90 preceding,但是您需要组装一个类似于以下内容的时间序列查询,以便 Window 函数对基于时间的行进行操作:

    SELECT c.card_id, t.amount, g.d as d_series
    FROM generate_series(
      '2017-04-06'::timestamp, '2017-07-06'::timestamp, '1 day'::interval
    ) g(d)
    CROSS JOIN ( SELECT distinct card_id from test ) c
    LEFT JOIN test t ON t.card_id = c.card_id and t.tran_dt = g.d
    ORDER BY c.card_id, d_series
    

    对于您的需求(根据您的问题描述),我会坚持使用group by

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-29
      • 2021-09-12
      • 1970-01-01
      • 2020-02-17
      • 1970-01-01
      • 1970-01-01
      • 2021-03-29
      • 1970-01-01
      相关资源
      最近更新 更多