使用WITH RECURSIVE查找所有复合类型组件类型

时间:2012-09-21 16:58:08

标签: sql postgresql recursive-cte

给定复合类型的typname,如何以递归方式找到所有组件类型的类型oids

示例:

CREATE TYPE t_station AS (x INT,
                          y INT,
                          label VARCHAR);

CREATE TYPE t_address AS (city VARCHAR,
                          street VARCHAR,
                          no INT,
                          stations t_station[]);

CREATE TYPE t_employee AS (name VARCHAR,
                           age INT,
                           coins INT[],
                           notes VARCHAR,
                           address t_address);

我可以获得oids成员的t_employee类型:

SELECT
   t.typname, t.oid, a.attname, a.atttypid
FROM
   pg_attribute a INNER JOIN pg_type t ON a.attrelid = t.typrelid
   AND t.typname = 't_employee'

但是我需要对此进行说明,我想可以使用WITH RECURSIVE

来完成
WITH RECURSIVE allattrs(typname, oid, attname, atttypid) AS (
  select t.typname, t.oid, a.attname, a.atttypid from pg_attribute a inner join pg_type t on a.attrelid = t.typrelid and t.typname = 't_employee'
  union all
  select z.* from
  (select t.typname, t.oid, a.attname, a.atttypid from pg_attribute a inner join pg_type t on a.attrelid = t.typrelid) z,
  allattrs y where y.atttypid = z.oid
)
SELECT * FROM allattrs limit 100
;

但是找不到t_station复合类型的内部数组。

1 个答案:

答案 0 :(得分:1)

数组类型会破坏您所遵循的简单链。如果是数组类型,则必须解析pg_type.typelem以获取基类型。

WITH RECURSIVE cte(typname, type_oid, attname, atttypid, typelem) AS (
   SELECT t.typname, t.oid, a.attname, a.atttypid, t.typelem
   FROM   pg_type t
   LEFT   JOIN pg_attribute a ON  a.attrelid = t.typrelid
                              AND a.attnum > 0
                              AND NOT a.attisdropped
   WHERE  t.typrelid = 't_employee'::regclass

   UNION ALL
   SELECT t.typname, t.oid
         ,COALESCE(a.attname, t.typelem::regtype::text)
         ,COALESCE(a.atttypid, t.typelem), t.typelem
   FROM   cte c
   JOIN   pg_type t ON t.oid = c.atttypid AND (t.typtype = 'c' OR t.typelem > 0)
   LEFT   JOIN pg_attribute a ON  a.attrelid = t.typrelid
                              AND a.attnum > 0
                              AND NOT a.attisdropped
   )
SELECT typname, type_oid, attname, atttypid
FROM   cte
WHERE  typelem = 0  -- filter out rows for array types

如果要在结果中包含数组类型的额外行,请删除最终的WHERE条件..

此JOIN条件仅遵循复合类型或数组:

AND (t.typtype = 'c' OR t.typelem > 0)

我还添加了排除系统列和死列的条件:

AND a.attnum > 0
AND NOT a.attisdropped

Details about catalog tables in the manual.