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.id
和 sub_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_rel
的10
-> sub_rel
的20
-> [=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_rel
和 LEFT 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)
假设我在 PostgreSQL 数据库中有以下 tables:
base_rel (id SERIAL, ...)
sub_rel (id SERIAL, base_id INT)
sub_sub_rel (id SERIAL, sub_id INT)
我需要在一个查询中查找所有继承路径(查找 base_rel.id
和 sub_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_rel
的10
-> sub_rel
的20
-> [=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_rel
和 LEFT 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)