【问题标题】:How to get the list of column names for all indexes on a table in PostgreSQL?如何获取 PostgreSQL 中表上所有索引的列名列表?
【发布时间】:2019-08-22 04:31:25
【问题描述】:

我有这个查询来获取表上的索引列表:

SELECT
    ns.nspname as schema_name,
    tab.relname as table_name,
    cls.relname as index_name,
    am.amname as index_type,
    idx.indisprimary as is_primary,
    idx.indisunique as is_unique
FROM
    pg_index idx
INNER JOIN pg_class cls ON cls.oid=idx.indexrelid
INNER JOIN pg_class tab ON tab.oid=idx.indrelid
INNER JOIN pg_am am ON am.oid=cls.relam
INNER JOIN pg_namespace ns on ns.oid=tab.relnamespace
WHERE ns.nspname = @Schema AND tab.relname = @Name

它似乎工作正常。但现在我需要查询列列表,但我无法理解系统视图的工作原理。

具体我要找的是:

  • [与第一个查询匹配的索引名称或 ID]
  • 索引中的顺序
  • 列名
  • 升序或降序
  • 排序的列或包含的列

理想情况下,我想一次获取给定表的所有索引的上述项目。


请注意,我寻找的不仅仅是列名。

【问题讨论】:

标签: postgresql database-indexes database-metadata


【解决方案1】:

使用系统目录信息函数pg_get_indexdef(index_oid) 获取完整信息(包括索引表达式列表) - 在针对pg_index 的查询中获取给定表的所有索引:

SELECT pg_get_indexdef(indexrelid) || ';' AS idx
FROM   pg_index
WHERE  indrelid = 'public.tbl'::regclass;  -- optionally schema-qualified

相关:

如果您依赖非限定表名(无架构),则您依赖于当前的 search_path 设置,并且可能会在不同架构中获得同名表的结果。

或者,您可以手动加入pg_attribute 以获取各个列,如这些相关答案中所示:

关键因素是这样加入:

FROM   pg_index idx
LEFT   JOIN pg_attribute a ON a.attrelid = idx.indrelid
                          AND a.attnum = ANY(idx.indkey)
                          AND a.attnum > 0

关于pg_index.indkey的手册:

这是一个由indnatts 值组成的数组,指示哪些表列 这个索引索引。例如,1 3 的值意味着 第一个和第三个表列组成索引条目。钥匙 列在非关键(包含)列之前。此数组中的零 表示对应的索引属性是一个表达式 表列,而不是简单的列引用。

添加AND a.attnum > 0 在技术上没有必要,因为没有a.attnum = 0。但它使查询更清晰,并且不会受到伤害。 The manual:

普通列从 1 开始编号。系统列,例如 oid,具有(任意)负数。

请注意,“列名列表” 实际上也可以包含表达式。而且由于 Postgres 11 还有“包含”列(那里没有表达式)。 pg_get_indexdef() 处理所有可能的并发症。

【讨论】:

  • 感谢您的警告。我见过的其他数据库首先需要表级别的计算列,您不能将计算直接放入索引中。
  • @JonathanAllen:Oracle、DB2 和某个 Firebird 也支持在表达式上创建索引。这不是 Postgres 独有的
  • @JonathanAllen:OTOH,Postgres 还不支持真正生成的列。在 Postgres 12 中添加该功能的工作正在进行中。请参阅:stackoverflow.com/a/8250729/939860
【解决方案2】:

正如 Erwin Brandstetter 所详述的那样,您可以从系统目录中将其拼凑起来。

这是一个查询,它将返回您想要的信息:

SELECT i.indexrelid::regclass AS indexname,                                    
       k.i AS index_order,                                                     
       i.indnkeyatts,                                                          
       coalesce(a.attname,                                                     
                (('{' || pg_get_expr(                                          
                            i.indexprs,                                        
                            i.indrelid                                         
                         )                                                     
                      || '}')::text[]                                          
                )[k.i]                                                         
               ) AS index_column,                                              
       i.indoption[k.i - 1] = 0 AS ascending,                                  
       k.i <= i.indnkeyatts AS is_key                                          
FROM pg_index i                                                                
   CROSS JOIN LATERAL unnest(i.indkey) WITH ORDINALITY AS k(attnum, i)         
   LEFT JOIN pg_attribute AS a                                                 
      ON i.indrelid = a.attrelid AND k.attnum = a.attnum                       
WHERE i.indrelid = 'schemaname.tablename'::regclass;

此查询只能从 PostgreSQL v11 开始工作(但在 v11 之前没有覆盖索引)。

此外,如果索引表达式包含逗号,则查询将失败;我不知道如何解决这个问题。

【讨论】:

  • 加入一个未嵌套的集合比我的简短a.attnum = ANY(idx.indkey) 更准确——以防(有问题!)索引表达式列表中的列重复)。但是你也有一些回归。 1.indnkeyatts:需要在第 11 页之前为 indnatts,第 11 页中缺少 included 列。 2. schema-qualify 'tablename'::regclass 以确保。 3. pg_get_expr() 返回一个索引表达式列表,其中可能包含使您的串联数组文字无效的逗号。考虑这个例子:to_tsvector('simple', col)。修复变得混乱。 pg_get_indexdef() 避免麻烦。
  • 我猜这个问题是关于 PostgreSQL v11 的,因为它询问“包含的列”。但是您对表达式中的逗号是正确的。我会看看我能不能做点什么。
【解决方案3】:

10.4 版

SELECT idx.indexrelid::regclass AS indexname,                                    
       k.i AS index_order,                                                     
       --i.indnkeyatts,                                                          
       coalesce(att.attname,                                                     
                (('{' || pg_get_expr(                                          
                            idx.indexprs,                                        
                            idx.indrelid                                         
                         )                                                     
                      || '}')::text[]                                          
                )[k.i]                                                         
               ) AS index_column,                                              
       pg_index_column_has_property(idx.indexrelid,k.i::int,'asc') AS ascending,                                  
       k.i != -1 AS is_key                                          
FROM pg_index idx                                                                
   CROSS JOIN LATERAL unnest(idx.indkey) WITH ORDINALITY AS k(attnum, i)         
   LEFT JOIN pg_attribute AS att                                                 
      ON idx.indrelid = att.attrelid AND k.attnum = att.attnum  

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-22
    • 1970-01-01
    • 2015-11-02
    • 2014-08-16
    • 1970-01-01
    • 1970-01-01
    • 2011-10-10
    • 2011-08-04
    相关资源
    最近更新 更多