5

我必须检索索引中涉及的列的顺序。使用函数 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()函数中获取值。

谢谢,

拉维

4

3 回答 3

8

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'

过时警告:从 PostgreSQL 9.6 开始,pg_am 上的列不再可用。

于 2013-08-08T14:18:36.973 回答
4

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

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

我上岸查看源代码src/backend/utils/adt/ruleutils.cfunction pg_get_indexdef_worker

这表明它首先测试是否pg_am.amcanorder为真,如果是则解码indoption.

假设您需要名为 的表的索引,这将为您提供可排序的列的 reloptions 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 LASTforASCNULLS FIRSTfor DESC

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

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

于 2013-08-08T14:03:39.057 回答
0

您可能需要的关于索引的一切。适用于 >=9.1 & <=9.5 的版本。

学分 - IRC 上的 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}        | 

想把它贴在这里,这样任何需要它的人就不必像我一样费力地寻找它。

于 2016-07-25T13:40:19.597 回答