连接后保留数组元素的顺序

Eri*_*low 4 postgresql cte array recursive

我有一个返回 CTE 的查询,如下所示

+-----------+-------------+
|   node_id | ancestors   |
|-----------+-------------|
|         1 | []          |
|         2 | []          |
|         3 | [1]         |
|         4 | [2]         |
|         5 | [4, 2]      |
+-----------+-------------+
Run Code Online (Sandbox Code Playgroud)

我想要做的是加入表nodes并将该列中的 id 替换ancestors为表中的另一列nodes。这是我到目前为止的查询:

WITH RECURSIVE tree AS (
  -- snip --
)
SELECT node.entity_id AS id,
       array_remove(array_agg(parent_nodes.entity_id), NULL) AS ancestors
FROM tree
JOIN entity.nodes AS node ON node.id = tree.node_id
LEFT OUTER JOIN entity.nodes AS parent_nodes ON parent_nodes.id = ANY(tree.ancestors)
GROUP BY node.id;
Run Code Online (Sandbox Code Playgroud)

该查询的问题在于它丢失了原始数组的顺序ancestors。有没有办法在函数执行期间保持原始顺序的同时执行连接array_agg

Erw*_*ter 5

您的查询的问题是连接条件id = ANY(ancestors)。它不仅不保留原始顺序,还消除了数组中的重复元素。( Anid可以匹配 中的 10 个元素ancestors,但仍然只会选择一次。)不确定查询的逻辑是否允许重复元素,但如果允许,我很确定您想要保留所有实例 - 您想要保留“毕竟是原始订单”。

假设当前的 Postgres 9.4+由于缺乏信息,我建议采用完全不同的方法:

SELECT n.entity_id, p.ancestors
FROM   tree t
JOIN   nodes n ON n.id = t.node_id
LEFT   JOIN LATERAL (
   SELECT ARRAY (
      SELECT p.entity_id
      FROM   unnest(t.ancestors) WITH ORDINALITY a(id, ord)
      JOIN   entity.nodes p USING (id)
      ORDER  BY ord
      ) AS ancestors
   ) p ON true;
Run Code Online (Sandbox Code Playgroud)

nodes.id仅当定义为主键且nodes.entity_id唯一时,您的查询才能按预期工作。问题中缺少信息。

通常,这个没有显式的更简单的查询ORDER BY也可以工作,但没有保证(Postgres 9.3+)...

SELECT n.entity_id, p.ancestors
FROM   tree t
JOIN   nodes n ON n.id = t.node_id
LEFT   JOIN LATERAL (
   SELECT ARRAY (
      SELECT p.entity_id
      FROM   unnest(t.ancestors) id
      JOIN   entity.nodes p USING (id)
      ) AS ancestors
   ) p ON true;
Run Code Online (Sandbox Code Playgroud)

您也可以确保安全。详细解释:

Postgres 9.3 的SQL Fiddle 演示。

可选优化

您加入entity.nodes两次 - 替换node_id等等ancestors。另一种方法是将两者折叠成一个数组或一组并仅连接一次。可能会更快,但你必须测试。
对于这些替代方案,我们在任何情况下都需要ORDER BY

在取消嵌套之前添加node_id到数组中...ancestors

SELECT p.arr[1] AS entity_id, p.arr[2:2147483647] AS ancestors
FROM   tree t
LEFT   JOIN LATERAL (
   SELECT ARRAY (
      SELECT p.entity_id
      FROM   unnest(t.node_id || t.ancestors) WITH ORDINALITY a(id, ord)
      JOIN   entity.nodes p USING (id)
      ORDER  BY ord
      ) AS arr
   ) p ON true;
Run Code Online (Sandbox Code Playgroud)

或者添加到我们加入之前node_id的未嵌套元素......ancestors

SELECT p.arr[1] AS entity_id, p.arr[2:2147483647] AS ancestors
FROM   tree t
LEFT   JOIN LATERAL (
   SELECT ARRAY (
      SELECT p.entity_id
      FROM  (
         SELECT t.node_id AS id, 0 AS ord
         UNION ALL
         SELECT * FROM unnest(t.ancestors) WITH ORDINALITY
         ) x
      JOIN   entity.nodes p USING (id)
      ORDER  BY ord
      ) AS arr
   ) p ON true;
Run Code Online (Sandbox Code Playgroud)

您没有显示我们的 CTE,这可能会进一步优化...