PostgreSQL 嵌套查询

PostgreSQL nested suqueries

假设我在 PostgreSQL 数据库中有以下 tables:

base_rel (id SERIAL, ...)
sub_rel (id SERIAL, base_id INT)
sub_sub_rel (id SERIAL, sub_id INT)

我需要在一个查询中查找所有继承路径(查找 base_rel.idsub_rel.id 假设我知道 sub_sub_rel.id)。天真的方法不起作用,因为您不能将计算值用作子查询的参数。所以下面的示例代码失败了:

SELECT 
    (SELECT sub_id FROM sub_sub_rel WHERE id = X) AS sub_id,
    (SELECT base_id FROM sub_rel WHERE id = sub_id) AS base_id;

但我需要在一次查询中知道这些 ID。继承树相对较大,所以我无法为每个 table 创建 PL/pgSQL 函数,但我可以动态构建查询。有没有办法获取这些 ID?

从具有自引用列的递归 table 开始——这些实体完全不同,例如“User extends Group extends Server default”。


P.S.:在一个查询中使用这些 ID 的原因是继承 table 数据如下:

 entity_type | entity_id | priority | value
-------------|-----------|----------|-------
  1          | 2         | 10       | xxx
  2          | 20        | 20       | yyy
  3          | 10        | 30       | zzz

当然,我可以构建这些 ID(sub_sub_rel10 -> sub_rel20 -> [=24= 的2 ]) 在应用程序端和具有最高优先级的获取值,但它需要 N 次数据库往返,因为每个 ID 都依赖于之前的 ID。所以我寻求一种在一次往返中完成此操作的方法。

鉴于您的模式,您可以使用双连接:

SELECT base_rel.id AS base_id,
       sub_rel.id AS sub_id,
       sub_sub_rel.id AS sub_sub_id

FROM sub_sub_rel

INNER JOIN sub_rel
ON sub_rel.id = sub_sub_rel.sub_id

INNER JOIN base_rel
ON base_rel.id = sub_rel.base_id;

任何 WHERE 子句在您的特定情况下都有意义。

请注意,此查询从 descendants 开始,一直到根,这意味着结果集中不包含没有子代或孙代的根元素。

如果你想从根开始往下走,特别是在可能没有两级后代的情况下,你需要 select FROM base_relLEFT OUTER JOIN 另外两个 tables:

SELECT base_rel.id AS base_id,
       sub_rel.id AS sub_id,
       sub_sub_rel.id AS sub_sub_id

FROM base_rel

LEFT OUTER JOIN sub_rel
ON sub_rel.base_id = base_rel.id

LEFT OUTER JOIN sub_sub_rel
ON sub_sub_rel.sub_id = sub_rel.id;

如果所有 table 都具有相同的架构(ID 列除外),那么您可以使用一个 table 和自引用列:

CREATE TABLE thing (
    id SERIAL PRIMARY KEY,
    parent_id INTEGER NULL REFERENCES thing(id)
);

使用 PostgreSQL 的递归查询支持,您仍然可以在一个查询中获取世代关系中的所有行,但如果需要,您将不得不使用应用程序端逻辑将它们构建到树中。

例如,通过 ID select 特定行但也包括所有祖先行:

WITH RECURSIVE thing_ancestors AS (
    SELECT thing.* FROM thing WHERE id = ?
    UNION
    SELECT thing.* FROM thing_ancestors
    INNER JOIN thing ON thing.id = thing_ancestors.parent_id
)
SELECT * FROM thing_ancestors;

您可以使用递归查询有效地沿路径聚合数据到根或叶(取决于查询的方向)。考虑以下树:

1
+- 2
+- 3
   +- 4
5
+- 6
7

这将用以下数据表示:

INSERT INTO thing VALUES
  (1,NULL),
    (2,1),
    (3,1),
      (4,3),
  (5,NULL),
    (6,5),
  (7,NULL);

现在我们可以使用以下递归查询获得 id = 4 项的路径,该查询从叶开始并向根前进,将最终路径留在根节点上(parent_id IS NULL).

WITH RECURSIVE thing_ancestors AS (
    SELECT thing.*, thing.id || '' AS path FROM thing WHERE id = 4
    UNION
    SELECT thing.*, thing.id || '/' || path AS path FROM thing_ancestors
    INNER JOIN thing ON thing.id = thing_ancestors.parent_id
)
SELECT path FROM thing_ancestors WHERE parent_id IS NULL;

这导致单行具有单个值 "1/3/4" - 完美! (See sqlfiddle)