【问题标题】:PostgreSQL: How to index all foreign keys?PostgreSQL:如何索引所有外键?
【发布时间】:2011-02-27 13:01:03
【问题描述】:

我正在使用一个大型 PostgreSQL 数据库,并且我正在尝试对其进行调整以获得更高的性能。

我们的查询和更新似乎使用外键进行大量查找。

我想要的是一种相对简单的方法,可以将索引添加到我们所有的外键中,而不必遍历每个表 (~140) 并手动进行。

在对此进行研究时,我发现无法让 Postgres 自动为您执行此操作(就像 MySQL 一样),但我也很乐意听到其他情况。

【问题讨论】:

    标签: sql database postgresql indexing foreign-keys


    【解决方案1】:

    编辑:所以,我写了下面的查询然后想......“等等,Postgresql 要求外键目标必须有唯一的索引。”所以我想我误解了你的意思?您可以使用以下查询来检查您的外键的 source 是否具有索引,方法是将“conrelid”替换为“confrelid”,将“conkey”替换为“confkey”(是的,是的,在查询...)

    好吧,我想应该可以通过系统目录...像往常一样,系统目录的最佳指南是使用 psql 并执行“\set ECHO_HIDDEN 1”,然后查看它生成的 SQL有趣的“\d”命令。这是用于查找表的外键的 SQL ("\d tablename"):

    -- $1 is the table OID, e.g. 'tablename'::regclass
    SELECT conname, conrelid::pg_catalog.regclass,
      pg_catalog.pg_get_constraintdef(c.oid, true) as condef
    FROM pg_catalog.pg_constraint c
    WHERE c.confrelid = $1 AND c.contype = 'f' ORDER BY 1;
    

    似乎 pg_constraint 有列 conkeyconfkey 看起来它们可能是定义键的列号。 confkey 可能是外表中的列号,因为它对于外键只有非空值。另外,我花了一段时间才意识到这是显示外键引用给定表的 SQL。这就是我们想要的。

    所以这个查询显示数据开始成形:

    select confrelid, conname, column_index, attname
    from pg_attribute
         join (select confrelid::regclass, conname, unnest(confkey) as column_index
               from pg_constraint
               where confrelid = 'ticket_status'::regclass) fkey
              on fkey.confrelid = pg_attribute.attrelid
                 and fkey.column_index = pg_attribute.attnum
    

    我将使用像 unnest 这样的 8.4 功能...如果没有你也许可以相处。

    我最终得到:

    select pg_index.indexrelid::regclass, 'create index ' || relname || '_' ||
             array_to_string(column_name_list, '_') || '_idx on ' || confrelid ||
             ' (' || array_to_string(column_name_list, ',') || ')'
    from (select distinct
           confrelid,
           array_agg(attname) column_name_list,
           array_agg(attnum) as column_list
         from pg_attribute
              join (select confrelid::regclass,
                     conname,
                     unnest(confkey) as column_index
                    from (select distinct
                            confrelid, conname, confkey
                          from pg_constraint
                            join pg_class on pg_class.oid = pg_constraint.confrelid
                            join pg_namespace on pg_namespace.oid = pg_class.relnamespace
                          where nspname !~ '^pg_' and nspname <> 'information_schema'
                          ) fkey
                   ) fkey
                   on fkey.confrelid = pg_attribute.attrelid
                      and fkey.column_index = pg_attribute.attnum
         group by confrelid, conname
         ) candidate_index
    join pg_class on pg_class.oid = candidate_index.confrelid
    left join pg_index on pg_index.indrelid = confrelid
                          and indkey::text = array_to_string(column_list, ' ')
    

    好的,这个怪物打印出候选索引命令并尝试将它们与现有索引匹配。因此,您可以简单地在末尾添加“where indexrelid is null”以获取创建似乎不存在的索引的命令。

    这个查询不能很好地处理多列外键;但恕我直言,如果您使用这些,那您就应该遇到麻烦。

    LATER EDIT:这是在顶部放置建议编辑的查询。因此,这显示了在作为外部来源的列上创建不存在的索引的命令键(不是它的目标)。

    select pg_index.indexrelid::regclass, 'create index ' || relname || '_' ||
             array_to_string(column_name_list, '_') || '_idx on ' || conrelid ||
             ' (' || array_to_string(column_name_list, ',') || ')'
    from (select distinct
           conrelid,
           array_agg(attname) column_name_list,
           array_agg(attnum) as column_list
         from pg_attribute
              join (select conrelid::regclass,
                     conname,
                     unnest(conkey) as column_index
                    from (select distinct
                            conrelid, conname, conkey
                          from pg_constraint
                            join pg_class on pg_class.oid = pg_constraint.conrelid
                            join pg_namespace on pg_namespace.oid = pg_class.relnamespace
                          where nspname !~ '^pg_' and nspname <> 'information_schema'
                          ) fkey
                   ) fkey
                   on fkey.conrelid = pg_attribute.attrelid
                      and fkey.column_index = pg_attribute.attnum
         group by conrelid, conname
         ) candidate_index
    join pg_class on pg_class.oid = candidate_index.conrelid
    left join pg_index on pg_index.indrelid = conrelid
                          and indkey::text = array_to_string(column_list, ' ')
    where indexrelid is null
    

    我的经验是,这并不是那么有用。它建议为确实不需要索引的参考代码等创建索引。

    【讨论】:

    • 在您的编辑之后,这看起来可以正常工作,我将在这个和 leonbloy 的答案之间进行比较
    • 这个不显示外键,只显示主键。这是没有意义的,因为您不需要 pk 的索引,因为 pk 约束“是”一个索引
    • @peperg 不,查询与主键没有任何直接关系:它找到外键的目标。当然,如果您的数据库设计良好,那么外键的目标就是主键。请注意顶部的编辑建议如何更改它以查找外键的 所在的位置'未编入索引(这实际上并不太有用恕我直言)。很抱歉造成混乱,答案可能应该重写。
    • 我有表“x”,列“id”,表“y”,列“x_id”。 x.id 是一个 PK,所以上面有一个 idnex。我有外键 y.x_id -> x.id 。我希望这段代码在 y.x_id 但不在 x.id 上生成索引(有一个 PK 索引)
    • @peperg 正确,所以将 confrelid 更改为 conrelidconfkey 更改为 conkey 以这种方式查看外键,并附加 where indexrelid is null 以仅显示不显示的索引不存在。我将更新答案以显示已编辑的查询。
    【解决方案2】:

    信息在catalog tables 中。但它似乎不是很简单,想要你需要,特别是如果已经创建了一些索引(以及多列索引呢......)

    如果你没有任何索引的 FK ,你可以做一些快速而肮脏的事情,因为

     SELECT 'CREATE INDEX ' || table_name || '_' || column_name || '_idx ON '
       || table_name || '(' || column_name || ');'
    from foreign_key_tables where schema = 'public';
    

    您将替换为您感兴趣的架构,将其转储到文件中,编辑、检查、祈祷并提供给 psql。请注意此过程未检测到已存在的索引。

    啊,foreign_key_tables 是一个信息视图,创建为:

    CREATE VIEW foreign_key_tables AS SELECT
        n.nspname AS schema,
        cl.relname AS table_name,
        a.attname AS column_name,
        ct.conname AS key_name,
        nf.nspname AS foreign_schema,
        clf.relname AS foreign_table_name,
        af.attname AS foreign_column_name,
        pg_get_constraintdef(ct.oid) AS create_sql
    FROM pg_catalog.pg_attribute a
    JOIN pg_catalog.pg_class cl ON (a.attrelid = cl.oid AND cl.relkind =
    'r')
    JOIN pg_catalog.pg_namespace n ON (n.oid = cl.relnamespace)
    JOIN pg_catalog.pg_constraint ct ON (a.attrelid = ct.conrelid AND
    ct.confrelid != 0 AND ct.conkey[1] = a.attnum)
    JOIN pg_catalog.pg_class clf ON (ct.confrelid = clf.oid AND clf.relkind
    = 'r')
    JOIN pg_catalog.pg_namespace nf ON (nf.oid = clf.relnamespace)
    JOIN pg_catalog.pg_attribute af ON (af.attrelid = ct.confrelid AND
    af.attnum = ct.confkey[1]);
    

    【讨论】:

    • 这看起来更像是我所追求的。可悲的是,我的表/列名称有点太长,无法直接运行它。让我玩一下。
    • leonbloy 的好解决方案!。这就是我正在寻找的,因为我无法在 nhibernate 映射文件 (hbm) 中创建这些索引。我最后的解决方案是执行 sql 脚本。
    【解决方案3】:

    我用这段代码创建了一个脚本,似乎有点短:

    SELECT 'DROP INDEX IF EXISTS fk_' || conname || '_idx; CREATE INDEX fk_' || conname || '_idx ON ' 
           || relname || ' ' || 
           regexp_replace(
               regexp_replace(pg_get_constraintdef(pg_constraint.oid, true), 
               ' REFERENCES.*$','',''), 'FOREIGN KEY ','','') || ';'
    FROM pg_constraint 
    JOIN pg_class 
        ON (conrelid = pg_class.oid)
    JOIN pg_namespace
        ON (relnamespace = pg_namespace.oid)
    WHERE contype = 'f'
      AND nspname = 'public'
      --AND 'fk_' || conname || '_idx' NOT IN (SELECT indexname FROM pg_indexes)
      ;
    

    如果您不想重新创建已经存在的索引,请在最后一行添加注释

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-21
      • 1970-01-01
      • 1970-01-01
      • 2017-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多