order by 在 POSTGRESQL 中的 partition by 子句中不起作用?

Posted

技术标签:

【中文标题】order by 在 POSTGRESQL 中的 partition by 子句中不起作用?【英文标题】:order by doesn`t work in partition by clause in POSTGRESQL? 【发布时间】:2021-06-03 04:31:49 【问题描述】:

我有一个这样的 postgresql 表:

我想用 id 来计算 cap = 0.0000 组的连续 dt 值,我试过这个:

select id ,dt,cap ,row_number() OVER ( partition by id,cap order by id,dt) t1 from tbl

我应该得到这样的结果:

id dt cap t1
10470 2020-12-01 00:00:00 0.0000 1
10470 2021-01-01 00:00:00 0.0000 2
10470 2021-02-01 00:00:00 0.0000 3
10470 2021-03-01 00:00:00 200.0000 1
10470 2021-04-01 00:00:00 0.0000 1
10470 2021-05-01 00:00:00 0.0000 2
10470 2021-06-01 00:00:00 0.0000 3

但实际上它的结果如下所示:

id dt cap t1
10470 2020-12-01 00:00:00 0.0000 1
10470 2021-01-01 00:00:00 0.0000 2
10470 2021-02-01 00:00:00 0.0000 3
10470 2021-04-01 00:00:00 0.0000 4
10470 2021-05-01 00:00:00 0.0000 5
10470 2021-06-01 00:00:00 0.0000 6
10470 2021-03-01 00:00:00 200.0000 1

如何得到我想要的结果?

【问题讨论】:

【参考方案1】:

这是一个空白和孤岛问题。您想要的逻辑是,当我们在给定的id 记录块中遇到非零的cap 值时,行号序列应始终从 1 重新开始,按dt 列的顺序排列。

解决此问题的一种方法是使用行数差异法:

WITH cte1 AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY dt) rn1,
              ROW_NUMBER() OVER (PARTITION BY id,
                                 CASE WHEN cap < 0.0001 THEN 0 ELSE 1 END
                                 ORDER BY dt) rn2
    FROM tbl
)

SELECT id, dt, cap,
       ROW_NUMBER() OVER (PARTITION BY id, rn2-rn1 ORDER BY dt) AS t1
FROM cte1
ORDER BY id, dt;

Demo

这里的一条评论是,我正在使用以下CASE 表达式来检测cap 列中的零或非零:

CASE WHEN cap < 0.0001 THEN 0 ELSE 1 END

请注意,我将“零”定义为小于0.0001。原因是浮点值在 Postgres 中并不精确。因此,由于“零”cap 值并非完全为零,因此以下 CASE 表达式可能不会按预期运行:

CASE WHEN cap = 0 THEN 0 ELSE 1 END

【讨论】:

以上是关于order by 在 POSTGRESQL 中的 partition by 子句中不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在聚合调用中不使用 ORDER BY 对 postgresql 自定义聚合中的行进行预排序?

如何在 PostgreSQL ORDER BY 子句中使用别名?

PostgreSQL ORDER BY 语句

PostgreSQL ORDER BY 语句

PostgreSQL order by 排序问题

PostgreSQL 可以在 ORDER BY 中使用 FOR LOOP 吗?