2010-06-03 42 views
14

我正在使用一个大型的PostgreSQL数据库,我试图调整它以获得更多的性能。PostgreSQL:如何索引所有外键?

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

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

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

回答

17

编辑:所以,我写了下面的查询,然后想......“等等,Postgresql要求外键目标必须有唯一索引。所以我想我误解了你的意思?你可以使用下面的查询来检查你的外键的源有索引,通过用“confrelid”代替“confrelid”和“conkey”代替“confkey”(是的,没错,在查询中没有别名...)

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

-- $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, ' ') 

OK,这个畸形打印出候选索引命令,并试图对其进行匹配与现有的指数。因此,您可以简单地在末尾添加“where indexrelid为null”以获取命令来创建似乎不存在的索引。

该查询不能很好地处理多列外键;但是如果你使用这些,你应该得到麻烦。

后编辑:这里是查询与建议编辑在顶部放在英寸所以这表明创建索引不存在的命令,在外键的来源列(不是它的目标)。

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 

我的经验是,这不是真的有用。它建议为诸如参考代码之类的东西创建索引,这些索引代码实际上不需要进行索引。

+0

编辑之后,这看起来好像可以工作,我要运行这个和leonbloy的回答之间的比较 – biggusjimmus 2010-06-04 16:19:40

+0

这一个不显示外键,但只有主键。这是没有意义的,因为你不需要pk的索引,因为pk约束“是”索引 – 2010-11-04 10:14:46

+0

@peperg否,查询直接与主键没有任何关系:它找到外键的目标。当然,如果你的数据库设计得很好,那么外键的目标就是主键。注意顶部的编辑表明如何改变它以找出外键的* source *没有被索引的地方实际上不是太有用的imho)。对于混淆抱歉,答案可能应该重写。 – araqnid 2010-11-05 00:46:55

5

该信息是在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]); 
+0

这看起来更像我以后。可悲的是,我的表/列名称太长,不能直接运行。让我玩一下。 – biggusjimmus 2010-06-04 16:17:51

+0

leonbloy!很好的解决方案!这是我正在寻找的,因为我不能在nhibernate映射文件(hbm)中创建这些索引。我最后的解决方案是执行sql脚本。 – 2011-08-08 00:47:59

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) 
    ; 

评论你不想重新创建已经存在的索引