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
);
我想要一个查询,它根据某些条件在 a1 或 a2 之间执行连接。类似于:
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 中存储的某些布尔条件有条件地连接 a1 或 a2 ?
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);
根据表 a1
和 a2
的大小以及您必须输出多少列,这可能会或可能不会比 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 )
;
我有两个 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
);
我想要一个查询,它根据某些条件在 a1 或 a2 之间执行连接。类似于:
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 中存储的某些布尔条件有条件地连接 a1 或 a2 ?
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);
根据表 a1
和 a2
的大小以及您必须输出多少列,这可能会或可能不会比 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 )
;