将UNION重写为JOIN

时间:2016-03-08 23:12:50

标签: sql postgresql

我有下表:

CREATE TABLE temp (
    grp         int,
    version     int,
    deleted     boolean not null,
    PRIMARY KEY (grp, version)
);

对于每个实体(grp),可以存在多个版本(version),版本号越大,版本的创建时间越近。由于各种原因,通常可以隐藏版本(deleted)。通常可能会隐藏整个实体,在这种情况下,version的所有grp都将为deleted

我想为每个实体选择/加入/过滤一行,其中该行是未删除的最新版本,如果删除了所有该实体的版本,则为最新版本。

我目前有一个使用union的解决方案,但我担心如果我尝试对union进行进一步的连接或过滤,性能会很差,我宁愿不必在每个中重复这些连接/过滤器联合查询。

下面的查询是否可以重写,以便不需要联合?

SELECT
    main.grp
    , main.version
    , main.deleted
--  , current_filter.version
--  , current_filter.deleted
FROM temp AS main
LEFT JOIN temp AS current_filter
    ON (
        current_filter.grp = main.grp
        AND current_filter.version > main.version
        AND NOT current_filter.deleted
    )
WHERE
    current_filter.version IS null
    AND NOT main.deleted
UNION
SELECT
    main.grp
    , main.version
    , main.deleted
--  , current_filter.version
--  , current_filter.deleted
--  , any_not_deleted.version
--  , any_not_deleted.deleted
FROM temp AS main
LEFT JOIN temp AS current_filter
    ON (
        current_filter.grp = main.grp
        AND current_filter.version > main.version
    )
LEFT JOIN temp AS any_not_deleted
    ON (
        any_not_deleted.grp = main.grp
        AND any_not_deleted.version < main.version
        AND NOT any_not_deleted.deleted
    )
WHERE
    current_filter.version IS null
    AND any_not_deleted.version IS null
    AND main.deleted
ORDER BY grp, version

SQLFiddle:http://sqlfiddle.com/#!15/f0b7d/1/0

2 个答案:

答案 0 :(得分:2)

在Postgres中,我认为最简单的解决方案使用distinct on

select distinct on (grp) t.*
from temp t
order by grp,
         deleted::int asc,
         version desc;

每组保留一行。该行是基于order by子句的第一行。

答案 1 :(得分:0)

使用row_number():

可以得到相同的结果
select t.grp, t.version , t.deleted
from
(select  t.grp, t.version , t.deleted
       , row_number() over (partition by grp order by deleted asc , version desc) as rnum
   from temp t) t
where rnum = 1         

SQLFiddle:http://sqlfiddle.com/#!15/934ba/11