PostgreSQL:如何索引所有外键?

时间:2010-06-03 21:52:52

标签: sql database postgresql indexing foreign-keys

我正在使用大型PostgreSQL数据库,我正在尝试调整它以获得更高的性能。

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

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

在研究这个问题时,我发现没有办法让Postgres自动为你做这件事(就像MySQL那样),但我也很乐意在那里听到。

3 个答案:

答案 0 :(得分:18)

编辑:所以,我在下面编写了查询,然后认为......“坚持下去,Postgresql要求外键目标必须具有唯一索引。”所以我想我误解了你的意思?您可以使用以下查询来检查外键的是否有索引通过将“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为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

我的经验是,这并不是真的有用。它建议为参考代码创建索引,而不需要将其编入索引。

答案 1 :(得分:6)

信息在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]);

答案 2 :(得分:0)

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

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)
  ;
如果您不想重新创建现有索引,请在最后一行注释