2

我在 PostgreSQL 中有一个表,其中包含:

id   name   arrayofparents
1     First
2     Second      {1}
3     Second_Sec  {1,2}
4     Third       {1,2,3}
5     Second_A    {1}
6     Other
7     Trash       {6}

arrayofparents是类型integer[],它包含该行的父记录列表,具有正确的顺序。

id=4父母是:First那么Second然后 Second_sec

我如何编写一个查询,对于任何给定的 id,它将生成一个其父母姓名的字符串?

例如:

id=3: First->Second.

id=4: First->Second->Second_sec.

id=7: Other.

编辑: 如果可能的话,我更喜欢请求的 idname将始终出现。 id=3: First->Second->Second_sec.

id=4: First->Second->Second_sec->Third.

id=7: Other->Trash.

id=6: Other.

4

3 回答 3

2

您可以结合多项操作,如 generate_subscripts 和 array 来获得结果:

with mtab as (
      SELECT id, name, array_append(arrayofparents,id) as arrayofparents,
      generate_subscripts(array_append(arrayofparents, id), 1) AS p_id FROM tab where id=2
)
select distinct array_to_string(
  array(
    select tab.name from tab join mtab t on tab.id=t.arrayofparents[t.p_id]
  ), '->'
) ;

现场示例 Sqlfiddle

或使用外连接结合任何:

SELECT coalesce(string_agg(p.name, '->') || '->' || t.name, t.name) AS parentnames
FROM tab AS t
  LEFT JOIN tab AS p ON p.id = ANY(t.arrayofparents)
 where t.id =7 
GROUP BY t.id, t.name

现场示例 Sqlfiddle

于 2015-12-06T09:04:00.280 回答
1

这些查询中的每一个都适用于单个 id 以及整个表。
您也可以只返回路径/完整路径或所有其他列。

SELECT t.*, concat_ws('->', t1.path, t.name) AS full_path
FROM   tbl t
LEFT   JOIN LATERAL (
   SELECT string_agg(t1.name, '->' ORDER  BY i) AS path
   FROM   generate_subscripts(t.arrayofparents, 1) i
   JOIN   tbl t1 ON t1.id = t.arrayofparents[i]   
   ) t1 ON true
WHERE  t.id = 4;  -- optional

或者,您可以将 移动ORDER BY到子查询 - 可能会快一点:

SELECT concat_ws('->', t1.path, t.name) AS full_path
FROM   tbl t, LATERAL (
   SELECT string_agg(t1.name, '->') AS path
   FROM  (
      SELECT t1.name
      FROM   generate_subscripts(t.arrayofparents, 1) i
      JOIN   tbl t1 ON t1.id = t.arrayofparents[i]
      ORDER  BY i
      ) t1
   ) t1
WHERE  t.id = 4;  -- optional

由于聚合发生在LATERAL子查询中,我们不需要GROUP BY外部查询中的步骤。

我们也不需要LEFT JOIN LATERAL ... ON true保留所有arrayofparents为 NULL 或空的行,因为由于聚合函数,LATERAL子查询总是返回一行。
LATERAL需要 Postgres 9.3

用于concat_ws()忽略串联中可能的 NULL 值。

SQL小提琴。

WITH OTDINALITY在 Postgres 9.4中让它变得更简单和更快:

SELECT t.*, concat_ws('->', t1.path, t.name) AS full_path
FROM   tbl t, LATERAL (
   SELECT string_agg(t1.name, '->' ORDER BY ord) AS path
   FROM   unnest(t.arrayofparents) WITH ORDINALITY a(id,ord)
   JOIN   tbl t1 USING (id)  
   ) t1
WHERE  t.id = 4;

详细解释:


UNION ALL第 9.3 页的变体

SELECT t1.full_path
FROM   tbl t, LATERAL (
   SELECT string_agg(name, '->') AS full_path
   FROM  (
      (
      SELECT name
      FROM   generate_subscripts(t.arrayofparents, 1) i
      JOIN   tbl ON id = t.arrayofparents[i]
      ORDER  BY i
      )
      UNION ALL SELECT t.name
      ) t1
   ) t1
WHERE  t.id = 4;
于 2015-12-06T13:12:14.447 回答
0

如果您只想要直接父母(而不是祖父母),那么这样的事情应该有效:

SELECT c.id, c.name, string_agg(p.name, '->') AS parentnames
FROM yourtable AS c
  LEFT JOIN yourtable AS p ON p.id = ANY c.parents
GROUP BY c.id, c.name
于 2015-12-06T08:41:01.520 回答