Postgres Select 来自 Table 基于查询结果

Postgres Select from a Table Based On Query Result

我有两个 table 具有相同的列,顺序相同。我希望根据子查询条件加入两个 table 之一。例如,假设我有以下架构:

CREATE TABLE b (
    bid SERIAL PRIMARY KEY,
    cid INT NOT NULL
);

CREATE TABLE a1 (
    aid SERIAL PRIMARY KEY,
    bid INT NOT NULL REFERENCES b
);

CREATE TABLE a2 (
    aid SERIAL PRIMARY KEY,
    bid INT NOT NULL REFERENCES b
);

我想要一个查询,它根据某些条件在 a1a2 之间执行连接。类似于:

WITH z AS (
  SELECT cid, someCondition FROM someTable
)
SELECT *
FROM CASE z.someCondition THEN a1 ELSE a2 END
JOIN b USING (bid)
WHERE cid = (SELECT cid FROM z);

但是,上面的方法不起作用。是否有一些方法可以根据 table z 中存储的某些布尔条件有条件地连接 a1a2 ?

SQL 语法不允许条件连接。 实现类似效果的最简单方法可能是在 plpgsql 函数中使用动态查询,它可能如下所示:

create function conditional_select(acid int, some_condition boolean)
returns table (aid int, bid int, cid int)
language plpgsql as $$
declare
    tname text;
begin
    if some_condition then tname = 'a1';
    else tname = 'a2';
    end if;

    return query execute format ($fmt$
        select a.aid, b.bid, b.cid 
        from %s a
        join b using(bid)
        where cid = %s;
        $fmt$, tname, acid);
end $$;


select * from conditional_select(1, true)

如果像您的示例一样,您只有几列要输出,则可以对每一列使用 CASE 语句:

SELECT CASE z.someCondition THEN a1.aid ELSE a2.aid END AS aid,
       CASE z.someCondition THEN a1.bid ELSE a2.bid END AS bid
FROM b
JOIN a1 ON a1.bid = b.bid
JOIN a2 ON a2.bid = b.bid
JOIN someTable z USING (cid);

根据表 a1a2 的大小以及您必须输出多少列,这可能会或可能不会比 Klin 的函数解决方案快,而 Klin 的解决方案本质上比普通 SQL 甚至更多,因为动态查询。鉴于 z.someCondition 已经是 boolean 值,CASE 评估将非常快。小表+几列=这个解决方案;大表 + 许多列 = Klin 的解决方案。

如果条件是排他性的(我希望它们是):只需执行 both 查询和 UNION ALL 它们,使用 smart union 构建:

WITH z AS (
  SELECT cid
        , (cid %3) AS some_condition -- Fake ... 
        FROM  b
  )
SELECT *
  FROM a1
  JOIN b USING (bid)
 WHERE EXISTS( SELECT * FROM z
        WHERE some_condition = 1 AND cid = b.cid )
UNION ALL
SELECT *
  FROM a2
  JOIN b USING (bid)
 WHERE EXISTS( SELECT * FROM z
        WHERE some_condition = 2 AND cid = b.cid )
        ;

实现相同功能的语法略有不同:

WITH z AS (
  SELECT cid
        , (cid %3) AS some_condition 
        FROM  b
)
SELECT *
  FROM a1
  JOIN b ON a1.bid = b.bid
        AND EXISTS( SELECT * FROM z
        WHERE some_condition = 1 AND cid = b.cid )
UNION ALL
SELECT *
  FROM a2
  JOIN b ON a2.bid = b.bid
        AND EXISTS( SELECT * FROM z
        WHERE some_condition = 2 AND cid = b.cid )
        ;