从数组创建字符串

时间:2015-12-06 07:46:16

标签: sql arrays postgresql unnest

我在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=3First->Second

id=4First->Second->Second_sec

id=7Other

修改 如果可能,我希望始终显示请求的ID nameid=3First->Second->Second_sec

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

id=7Other->Trash

id=6Other

3 个答案:

答案 0 :(得分: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]
  ), '->'
) ;

live example 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

live example Sqlfiddle

答案 1 :(得分: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为空或空的所有行,因为LATERAL子查询总是返回一行由于集合功能。
LATERAL需要Postgres 9.3

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

SQL Fiddle.

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;

详细说明:

对于第9页的UNION ALL变体

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;

答案 2 :(得分: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