返回包含所有三个序列号的ID的所有行

时间:2017-05-28 20:27:50

标签: sql postgresql plpgsql relational-division

我有一张看起来像这样的表:

table1

+----+-----+------+
| id | seq | test |
+----+-----+------+
|  1 |   1 | HR   |
|  1 |   2 | RR   |
|  2 |   1 | HR   |
|  2 |   2 | RR   |
|  2 |   3 | OXY  |
|  3 |   1 | HR   |
|  3 |   2 | RR   |
|  4 |   1 | HR   |
|  4 |   2 | RR   | 
|  4 |   3 | OXY  | 
+----+-----+------+

我想得到如下的结果表。也就是说,只有当特定id的所有三个seq编号都存在时,我才需要拥有特定id的所有行:

+----+-----+------+
| id | seq | test |
+----+-----+------+
|  2 |   1 | HR   |
|  2 |   2 | RR   |
|  2 |   3 | OXY  |
|  4 |   1 | HR   |
|  4 |   2 | RR   |
|  4 |   3 | OXY  | 
+----+-----+------+

我期待写一个plpgsql函数,它给了我解决方案。我对plpgsql和编程一般都比较新。如果有人帮助我获得结果会很棒。

到目前为止,这是我的功能看起来像是不完整的:

CREATE OR REPLACE FUNCTION test()
returns SETOF table1 AS $$
DECLARE
    cur CURSOR FOR
        SELECT *
        FROM table1
        ORDER by id;
    rec_cur RECORD;
    counter INTEGER DEFAULT 0;

BEGIN 
    OPEN cur;

    FETCH FIRST FROM cur INTO rec_cur;
    MOVE RELATIVE +1 FROM cur;

    LOOP
        FETCH cur INTO rec_cur;
        EXIT WHEN NOT FOUND;

        IF rec_cur.seq = 1 AND counter = 0 THEN
        RETURN NEXT rec_cursor;
        END IF;

    END LOOP;
    CLOSE cur;
    RETURN;

END ; $$ 
LANGUAGE PLPGSQL STABLE PARALLEL SAFE;

2 个答案:

答案 0 :(得分:2)

游标绝对不是正确的方法。您可以使用聚合和having

轻松地使用ID
select id
from t
where seq in (1, 2, 3)
group by id
having count(seq) = 3;

然后获取原始行,有多种方法:

select t.*
from t join
     (select id
      from t
      where seq in (1, 2, 3)
      group by id
      having count(seq) = 3
     ) tt
     on t.id = tt.id;

编辑:

如果序列号始终从1开始并且没有间隙,则可以使用窗口函数:

select t.*
from (select t.*, max(t.seq) over (partition by t.id) as maxseq
      from t
     ) t
where maxseq = 3;

答案 1 :(得分:1)

你的问题不完整 如果我们可以假设存在包含seq = 1seq = 2的行,如果同一行seq = 3有一行id,那么它就会变为便宜又简单:

SELECT *
FROM  (SELECT id FROM table1 WHERE seq = 3) x
JOIN   table1 t USING (id)
-- ORDER BY id, seq;  -- unclear whether you need sorted output.

还假设要定义(id, seq) UNIQUE并且NOT NULL列。

如果您需要优化阅读效果,请添加部分索引:

CREATE INDEX foo ON table1 (id) WHERE seq = 3;

Since Postgres 9.6 this can be used in an index-only scan.

当然,您需要(id)的索引。 (id, seq)上的索引(如果您已说过UNIQUE约束,则存在该作业)。相关:

无论哪种方式,都是的情况。如果我们不能在id中假设顺序值,那么这里有一套识别合格seq的技术: