【问题标题】:Find IDs with only one observation in PostgreSQL在 PostgreSQL 中查找只有一个观察值的 ID
【发布时间】:2021-03-27 08:14:41
【问题描述】:

我有下表:

CREATE TABLE my_table (
    the_visitor_id varchar(5) NOT NULL, 
    the_visitor_visit timestamp NOT NULL,
    the_visitor_returning text
);

INSERT INTO my_table
VALUES ('VIS01', '2019-05-02 09:00:00','YES' ), 
       ('VIS01', '2019-05-04 12:00:00',NULL ), 
       ('VIS01', '2019-05-05 18:00:00',NULL ), 
       ('VIS02', '2019-05-06 18:30:00',NULL), 
       ('VIS02', '2019-05-15 12:00:00',NULL), 
       ('VIS03', '2019-06-30 18:00:00','YES'),
       ('VIS04', '2019-06-30 18:00:00','NULL');

我想过滤掉所有只有一个观察(或记录)的visitor_id。在这种情况下 VIS03 和 VIS04,所以我必须以 VIS01 和 VIS02 结束。我试过这个:

SELECT DISTINCT ON(the_visitor_id) the_visitor_id, 
the_visitor_visit, the_visitor_returning 
FROM my_table

预期的结果应该是:

the_visitor_id   the_visitor_visit    the_visitor_returning
VIS01            2019-05-02 09:00:00  YES
VIS01            2019-05-04 12:00:00    
VIS01            2019-05-05 18:00:00    
VIS02            2019-05-06 18:30:00    
VIS02            2019-05-15 12:00:00       

但我想需要像等级这样的东西。任何帮助将不胜感激。

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    可能还有其他方法可以做到这一点,但如果您创建一个仅包含超过 1 行的 visitor_ids 的派生表 CTE,然后在表的连接中使用它。显然,如果 my_table 很大,则索引会提高性能。

    WITH cte
    AS (
        SELECT the_visitor_id
        FROM my_table
        GROUP BY the_visitor_id
        HAVING count(*) > 1
        )
    SELECT my_table.*
    FROM my_table
    INNER JOIN cte ON cte.the_visitor_id = my_table.the_visitor_id
    

    【讨论】:

    • 你好@Ed Mendez,谢谢。它工作正常,但如果只有 CTE 中的一列。祝你有美好的一天!
    【解决方案2】:

    EXISTS 可以使用索引:

    SELECT the_visitor_id, the_visitor_visit, the_visitor_returning 
    FROM   my_table t1
    WHERE  EXISTS (
       SELECT FROM my_table
       WHERE  the_visitor_id = t1.the_visitor_id
       AND    ctid <> t1.ctid
       );
    

    使用ctid,因为您没有透露 PK 或表的任何 UNIQUE 列。关于ctid

    理想情况下,您应该在(the_visitor_id, any_notnull_column) 上有一个UNIQUE 索引并在查询中使用该列。比完整的顺序扫描、计数、连接(另一个 seq 或 idx 扫描)快得多。

    除非有任何可用的索引,否则使用 窗口函数 可以让我们至少将其保持为单次顺序扫描:

    SELECT the_visitor_id, the_visitor_visit, the_visitor_returning 
    FROM  (
        SELECT *, count(*) OVER (PARTITION BY the_visitor_id) AS ct
        FROM   my_table
        ) sub
    WHERE  ct > 1;
    

    db小提琴here

    【讨论】:

      猜你喜欢
      • 2020-03-11
      • 1970-01-01
      • 1970-01-01
      • 2014-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多