如何从Postgresql获取索引列顺序(ASC,DESC,NULLS FIRST ....)?

时间:2013-08-08 08:15:26

标签: postgresql

我必须检索索引中涉及的列的顺序。使用函数pg_get_indexdef()我可以得到索引的定义,如下所示,

"CREATE INDEX test ON ravi1.table_with_index USING btree ("Column1" DESC, "Column3" DESC, "Column4") WITH (fillfactor=60)"

这里定义说Column1和Column3是降序,Column4是升序。

使用String中的数据,我必须进行解析以获得列排序顺序。

是否有其他替代方法,以便我能够获得值,即。列顺序。

现在使用以下查询获取与各个索引关联的列

SELECT ARRAY(SELECT pg_get_indexdef(idx.indexrelid, k + 1, true) FROM
generate_subscripts(idx.indkey, 1) as k ORDER BY k ) as index_members,
idx.indexprs IS NOT NULL as indexprs
FROM pg_index as idx
JOIN pg_class as i ON i.oid = idx.indexrelid
JOIN pg_namespace as ns ON ns.oid = i.relnamespace
JOIN pg_class as t ON t.oid = idx.indrelid
where ns.nspname = 'schema' and t.relname ='table' and i.relname ='index'

在同一个查询中,是否也需要注意列顺序?

这将是一个很好的帮助,否则我必须编写一些解析器来获取pg_get_indexdef()函数的值。

谢谢,

拉​​维

3 个答案:

答案 0 :(得分:4)

JDBC驱动程序使用更简单的查询,它确实返回列是否定义为ASC或DESC

以下内容或多或少是驱动程序源代码的逐字副本。我删除了一些只有JDBC的列,使它更“通用”。

SELECT ct.relname AS TABLE_NAME, 
       i.indisunique, 
       ci.relname AS INDEX_NAME, 
       (i.keys).n AS ORDINAL_POSITION, 
       pg_catalog.pg_get_indexdef(ci.oid, (i.keys).n, false) AS COLUMN_NAME, 
       CASE am.amcanorder 
         WHEN true THEN CASE i.indoption[(i.keys).n - 1] & 1 
           WHEN 1 THEN 'DESC' 
           ELSE 'ASC' 
         END 
         ELSE NULL 
       END AS ASC_OR_DESC,
      pg_catalog.pg_get_expr(i.indpred, i.indrelid) AS FILTER_CONDITION 
FROM pg_catalog.pg_class ct 
  JOIN pg_catalog.pg_namespace n ON (ct.relnamespace = n.oid) 
  JOIN (SELECT i.indexrelid, i.indrelid, i.indoption, 
          i.indisunique, i.indisclustered, i.indpred, 
          i.indexprs, 
          information_schema._pg_expandarray(i.indkey) AS keys 
        FROM pg_catalog.pg_index i) i 
    ON (ct.oid = i.indrelid) 
  JOIN pg_catalog.pg_class ci ON (ci.oid = i.indexrelid) 
  JOIN pg_catalog.pg_am am ON (ci.relam = am.oid) 
WHERE n.nspname = 'some_schema'
AND ct.relname = 'some_table'

答案 1 :(得分:3)

在您的应用中提出这样的查询是保证未来维护者永远恨你的好方法。如果您必须这样做,请在数据库中定义一个至少可以轻松更改的视图 - 并且请在information_schema中为此提出一个新条目,以便可以在一个不错的地方访问它未来的理智。

我对你遇到麻烦并不感到惊讶。重新阅读pg_indexpg_am等文档后,我认为它是indoption。这很容易通过创建两个相同的索引来确认,一个asc,一个desc。确保正确解释它们......

我着手查看源代码src/backend/utils/adt/ruleutils.c函数pg_get_indexdef_worker

这表明它首先测试pg_am.amcanorder是否为真,如果是,则对indoption中的位进行解码。

这将为您提供可订购列的重定位,假设您需要名为blah2的表的索引:

SELECT
  i.relname, i.indrelid, k AS ordinalpos, i.indoption[k-1]
FROM (
  SELECT 
    pg_class.relname, 
    pg_index.indrelid, pg_index.indclass, pg_index.indoption,
    unnest(indkey) as k
  FROM pg_index
  INNER JOIN pg_class ON pg_index.indexrelid = pg_class.oid
  WHERE pg_index.indrelid = 'blah2'::regclass
) i
INNER JOIN pg_opclass on (pg_opclass.oid = i.indclass[k-1]) 
INNER JOIN pg_am ON (pg_opclass.opcmethod = pg_am.oid)
WHERE pg_am.amcanorder;

索引选项位的定义在src/include/catalog/pg_index.h

/*
 * Index AMs that support ordered scans must support these two indoption
 * bits.  Otherwise, the content of the per-column indoption fields is
 * open for future definition.
 */
#define INDOPTION_DESC                  0x0001  /* values are in reverse order */
#define INDOPTION_NULLS_FIRST   0x0002  /* NULLs are first instead of last */

因为它们没有在SQL级别公开,所以不能依赖于此而不会更改。使用此信息可能会导致您的应用程序在PostgreSQL升级后停止工作。虽然JDBC驱动程序使用它们作为马指出,但是如果不经过深思熟虑就不可能改变它们。

您可以像这样解码asc / desc位:

CASE WHEN i.indoption[k-1] & 1 = 1 THEN 'DESC' ELSE 'ASC' END AS descasc,

但你还必须处理空的第一位/最后一位,其含义根据它是升序还是降序来翻转:

CASE WHEN (i.indoption[k-1] & 2 = 2) THEN 'NULLS FIRST' ELSE 'NULLS LAST' END

但是一旦你开始考虑其他索引访问方法/操作,不可订购的索引(所以你不能只是内部连接和过滤)等等,它会变得混乱。最终我降临了:

SELECT
      t.relname AS tablename,
      i.relname AS indexname, pg_attribute.attname AS colname,
      k AS col_order,
      CASE WHEN NOT amcanorder THEN '' WHEN i.indoption[k-1] & 1 = 1 THEN 'DESC' ELSE 'ASC' END AS descasc,
      CASE WHEN NOT amcanorder THEN '' WHEN (i.indoption[k-1] & 2 = 2) THEN 'NULLS FIRST' ELSE 'NULLS LAST' END AS nulls
    FROM (
      SELECT
        pg_class.relname,
        pg_index.indrelid, pg_index.indclass, pg_index.indoption,
        unnest(pg_index.indkey) AS k
      FROM pg_index
      INNER JOIN pg_class ON pg_index.indexrelid = pg_class.oid
      WHERE pg_index.indrelid = 'blah2'::regclass
    ) i
    INNER JOIN pg_opclass on (pg_opclass.oid = i.indclass[k-1])
    INNER JOIN pg_am ON (pg_opclass.opcmethod = pg_am.oid)
    INNER JOIN pg_class t ON i.indrelid = t.oid
    INNER JOIN pg_attribute ON (pg_attribute.attrelid = i.indrelid AND pg_attribute.attnum = k);

...但尚未针对GiST,GIN,自定义索引方法,所有索引定义变体等进行全面测试。它肯定不会处理:

  • 唯一索引
  • 自定义归类
  • 部分索引
  • 省略ASCNULLS LASTASCNULLS FIRSTDESC

可能还有更多。当然,你也想要参数化表名过滤器。

@a_horsE_with_no_name可能有正确的想法:使用JDBC驱动程序的查询并完成它。

答案 2 :(得分:0)

您可能需要的关于索引的所有内容。适用于版本> = 9.1& < = 9.5

Credits - IRCI上的RhodiumToad。 :)

SELECT schemaname, tablename, indexname, amname, indisunique, indisprimary,
       array_agg(attname ORDER BY ord) AS columns,
       array_agg(coll ORDER BY ord) AS collations,
       array_agg(opclass ORDER BY ord) AS opclasses,
       array_agg(ordering ORDER BY ord) AS orderings,
       array_agg(expression ORDER BY ord) AS expressions,
       predicate
  FROM (SELECT n.nspname AS schemaname,
               ct.relname AS tablename,
               c.relname AS indexname,
               m.amname,
               s.indisunique, s.indisprimary, s.ord,
               a.attname,
               CASE WHEN con.nspname is not null
                    THEN format('%I.%I',con.nspname,co.collname)
               END AS coll,
               CASE WHEN oc.opcname is not null
                    THEN format('%I.%I',ocn.nspname,oc.opcname)
               END AS opclass,
               CASE WHEN m.amcanorder
                    THEN format('%s NULLS %s',
                           CASE (option & 1) WHEN 1 THEN 'DESC' ELSE 'ASC' END,
                           CASE (option & 2) WHEN 2 THEN 'FIRST' ELSE 'LAST' END)
               END AS ordering,
               pg_get_expr(s.indpred, s.indrelid) AS predicate,
               pg_get_indexdef(s.indexrelid, ord, false) AS expression
          FROM (SELECT *,
                       generate_series(1,array_length(i.indkey,1)) AS ord,
                       unnest(i.indkey) AS key,
                       unnest(i.indcollation) AS coll,
                       unnest(i.indclass) AS class,
                       unnest(i.indoption) AS option
                  FROM pg_index i) s
               JOIN pg_class c ON (c.oid=s.indexrelid)
               JOIN pg_class ct ON (ct.oid=s.indrelid)
               JOIN pg_namespace n ON (n.oid=c.relnamespace)
               JOIN pg_am m ON (m.oid=c.relam)
               LEFT JOIN pg_attribute a ON (a.attrelid=s.indrelid AND a.attnum=s.key)
               LEFT JOIN pg_collation co ON (co.oid=s.coll)
               LEFT JOIN pg_namespace con ON (con.oid=co.collnamespace)
               LEFT JOIN pg_opclass oc ON (oc.oid=s.class)
               LEFT JOIN pg_namespace ocn ON (ocn.oid=oc.opcnamespace)
       ) s2
 WHERE tablename = 'your_table_name'
 GROUP BY schemaname, tablename, indexname, amname, indisunique, indisprimary, predicate;

输出:

schemaname |         tablename          |            indexname            | amname | indisunique | indisprimary | columns |                     collations                      |                 opclasses                 |               orderings               | expressions | predicate 
------------+----------------------------+---------------------------------+--------+-------------+--------------+---------+-----------------------------------------------------+-------------------------------------------+---------------------------------------+-------------+-----------
 public     | ticket26180_indexes_spamin | test26180_indexes_spamin_a_hsh  | hash   | f           | f            | {a}     | {"pg_catalog.\"default\""}                          | {pg_catalog.text_ops}                     | {NULL}                                | {a}         | 
 public     | ticket26180_indexes_spamin | test26180_indexes_spamin_atpata | btree  | f           | f            | {a,b}   | {"pg_catalog.\"default\"","pg_catalog.\"default\""} | {pg_catalog.text_ops,pg_catalog.text_ops} | {"DESC NULLS FIRST","ASC NULLS LAST"} | {a,b}       | 
 public     | ticket26180_indexes_spamin | test26180_indexes_spamin_b_hsh  | hash   | f           | f            | {b}     | {"pg_catalog.\"default\""}                          | {pg_catalog.text_ops}                     | {NULL}                                | {b}         | 
 public     | ticket26180_indexes_spamin | ticket26180_a_6fe9a5_idx        | btree  | f           | f            | {a,b}   | {"pg_catalog.\"default\"","pg_catalog.\"default\""} | {pg_catalog.text_ops,pg_catalog.text_ops} | {"ASC NULLS LAST","ASC NULLS LAST"}   | {a,b}       | 
 public     | ticket26180_indexes_spamin | ticket26180_indexes_spamin_pkey | btree  | t           | t            | {id}    | {NULL}                                              | {pg_catalog.int4_ops}                     | {"ASC NULLS LAST"}                    | {id}        | 

想把它发布在这里,以便任何需要它的人都不必像我那样努力寻找它。