如何使用窗口函数仅在 POSTGRES 中选择不超过某个值的行

Posted

技术标签:

【中文标题】如何使用窗口函数仅在 POSTGRES 中选择不超过某个值的行【英文标题】:How to select rows up to a certain value only in POSTGRES using window function 【发布时间】:2021-03-05 22:35:00 【问题描述】:

我有一个名为 test 的表,并且只想选择它直到 1st 相同 id ('1144QQT') 的删除语句上方的行。所以,从 2021-03-01 到 2021-06-02(只有粗体)。因此,这不包括带有删除语句的行。它确实包含更多不同类型的 id 和更多相同类型的 id。

表架构:

CREATE TABLE test(
id                BIGINT       PRIMARY KEY NOT NULL,
barcode_id        VARCHAR      NOT NULL,
date              DATE         NOT NULL,
keyword           VARCHAR      NOT NULL
);

INSERT INTO test
VALUES
(1, '1144QQT', '2021-03-01'::date, 'insert'),
(2, '1144QQT', '2021-03-01'::date, 'insert'),
(3, '1144QQT', '2021-03-01'::date, 'insert'),
(4, '1144QQT', '2021-03-01'::date, 'insert'),
(5, '1144QQT', '2021-03-01'::date, 'insert'),
(6, '1144QQT', '2021-03-01'::date, 'insert'),
(7, '1144QQT', '2021-03-01'::date, 'insert'),
(8, '1144QQT', '2021-03-01'::date, 'insert'),
(9, '1144QQT', '2021-03-01'::date, 'insert'),
(10, '1144QQT', '2021-03-01'::date, 'insert'),
(11, '1144QQT', '2021-03-01'::date, 'insert');
id barcode_id date keyword
1 1144QQT 2021-03-01 insert
2 1144QQT 2021-03-02 adjust
3 5588aTT 2021-03-03 delete
4 4477aTT 2021-03-04 adjust
5 5588aTT 2021-03-05 adjust
6 1144QQT 2021-03-06 adjust
7 1144QQT 2021-03-07 delete
8 1144QQT 2021-03-08 insert
9 1144QQT 2021-03-09 adjust
10 1144QQT 2021-03-10 delete
11 4477aTT 2021-03-11 delete

所以,我期待的输出是这样的:

id barcode_id date keyword
1 1144QQT 2021-03-01 insert
2 1144QQT 2021-03-02 adjust
6 1144QQT 2021-03-06 adjust

如何使用 postgres 做到这一点?是否可以使用窗口函数来做到这一点?

【问题讨论】:

您似乎已经知道存在窗口函数并且可以在此处使用它们,因为您对其进行了标记(好吧,您尝试了但在单词之间添加了一个空格而不是连字符,我为您更正了)。所以你应该继续自己尝试一些东西。如果这不能解决问题,那么edit 问题并包含您的合理 尝试以及详细 解释(错误消息、意外结果等) .). @Kraigolas 哈哈哈是的,我想我知道 where 语句是什么。我编辑了问题以使其更清楚,或者我希望如此。 @sticky bit 我的意思是我试过了。该语句基本上是更大查询的子查询。所以,我只是想简化它。虽然想不通。 【参考方案1】:

根据您的描述,这个逻辑似乎可以满足您的需求:

select t.*
from t
where t.id = '1144QQT' and t.keyword <> 'delete';

这假设删除后 id 没有行 - 但这似乎是合理的,并且与您的示例数据一致。

对于您的具体问题,一种方法是相关子查询:

select t.*
from t
where t.id = '1144QQT' and
      t.id < (select min(t2.date)
              from t t2
              where t2.id = t.id and
                    t2.keyword <> 'delete'
             );

上述版本要求有一个“删除”——根据你的问题不清楚这是否是一个要求。另一种使用窗口函数:

select t.*
from (select t.*,
             min(date) filter (where keyword = 'delete') over (partition by id) as min_delete_date
      where t.id = '1144QQT' 
     ) t
where date < min_delete_date;

如果你想要所有行如果没有“删除”,那么将or min_delete_date is null添加到外部where

【讨论】:

您好,但它确实包含更多不同类型的 id 和更多相同类型的 id。我只想将它选择到相同 id 的第一个删除语句上方的行。因此,从 2021-03-01 到 2021-03-06,不包括使用 delete 关键字的行。 类似最后一个解决方案的东西实际上是我正在寻找的。感谢您的帮助。【参考方案2】:

使用SUM()窗口函数,用keyword = 'delete'识别第一行之前的行:

SELECT id, date, keyword
FROM (
  SELECT *, SUM((keyword = 'delete')::int) OVER (ORDER BY date) grp
  FROM tablename
  WHERE id = '1144QQT'
) t
WHERE grp = 0

请参阅demo。 结果:

id date keyword
1144QQT 2021-03-01 00:00:00 insert
1144QQT 2021-03-02 00:00:00 adjust
1144QQT 2021-03-06 00:00:00 adjust

【讨论】:

这也是一个不错的解决方案。非常感谢。【参考方案3】:

我只想将它选择到相同 id 的第一个删除语句上方的行。

NOT EXISTS() 救援:(注意 date 是一个保留字,并且是一个列名)

SELECT *
FROM ztable zt
WHERE zt.zid = '1144QQT'
AND NOT EXISTS (
    SELECT * FROM ztable nx
    WHERE nx.zid = zt.zid
    AND nx.zkeyword = 'delete'
    AND nx.zdate <= zt.zdate
    );

【讨论】:

以上是关于如何使用窗口函数仅在 POSTGRES 中选择不超过某个值的行的主要内容,如果未能解决你的问题,请参考以下文章

如何在带有 Postgres 的动态框架中使用窗口函数中的列值?

使用具有不同 order by 子句的 postgres 窗口函数

如何在 Postgres 的窗口函数中获取 mode()?

如何使用窗口函数枚举 Postgres 表中的分区组?

独立 CKFinder:如何仅在文件对话框弹出窗口中选择图像?

Postgres - 如何对窗口函数列的每 x 行求和?