2010-06-03 43 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) 
    ; 

評論你不想重新創建已經存在的索引