2010-06-03 16 views
14

Estoy trabajando con una gran base de datos PostgreSQL, y estoy intentando sintonizarla para obtener más rendimiento.PostgreSQL: ¿Cómo indexar todas las claves externas?

Nuestras consultas y actualizaciones parecen estar realizando muchas búsquedas utilizando claves externas.

Lo que me gustaría es una forma relativamente simple de agregar índices a todas nuestras claves externas sin tener que pasar por todas las tablas (~ 140) y hacerlo manualmente.

Al investigar esto, he llegado a la conclusión de que no hay forma de que Postgres lo haga automáticamente (como MySQL lo hace), pero me alegraría saber que también existe.

Respuesta

17

EDIT: entonces, escribí la consulta a continuación y luego pensé ... "Espera, Postgresql requiere que los objetivos de claves extranjeras tengan índices únicos". ¿Entonces supongo que entendí mal lo que querías decir? Puede usar la consulta siguiente para verificar que la fuente de sus claves externas tenga índices al sustituir "conrelid" por "confrelid" y "conkey" por "confkey" (sí, sí, sin alias en la consulta ...)

Bueno, supongo que debería ser posible revisar los catálogos del sistema ... Como de costumbre, la mejor guía para los catálogos del sistema es usar psql y hacer "\ set ECHO_HIDDEN 1" y luego ver qué SQL genera para interesantes comandos "\ d". Aquí está el SQL que se utiliza para encontrar las claves foráneas de una tabla ("\ d nombre de tabla"):

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

Parece que pg_constraint tiene columnas conkey y confkey que parecen que podrían ser los números de las columnas que la clave se define a través de . Probablemente confkey son los números de columna en la tabla foránea ya que solo no son nulos para claves externas. Además, me llevó un tiempo darme cuenta de que este es el SQL para mostrar las claves externas haciendo referencia a en la tabla dada. Que es lo que queremos de todos modos.

Así que algo esta consulta muestra los datos empezando a tomar forma:

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 

Voy a utilizar 8.4 características como unnest ... que podría ser capaz de llevarse bien sin él.

que terminó con:

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

bien, esta monstruosidad imprime los comandos de índice candidatos y trata de ellos coinciden con los índices existentes. Entonces, simplemente puede agregar "donde indexrelid es nulo" al final para que los comandos creen índices que no parecen existir.

Esta consulta no trata muy bien con claves externas de varias columnas; pero si estás usando eso, te mereces problemas. EDITAR

TARDE:. Aquí está la consulta con las modificaciones propuestas en la parte superior puesto en Así que esto muestra los comandos para crear índices que no existen, en las columnas que son la fuente de una clave externa (no su objetivo).

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 

Mi experiencia es que esto no es realmente tan útil. Sugiere crear índices para cosas como códigos de referencia que realmente no necesitan ser indexados.

+0

Después de su edición, esto parece que podría trabajar, voy a ejecutar una comparación entre esta y la respuesta de leonbloy – biggusjimmus

+0

Este no muestra claves externas, solo claves primarias. No tiene sentido porque no necesita índice para pk porque la restricción pk "es" un índice –

+0

@peperg no, la consulta no tiene nada que ver con las claves primarias directamente: encuentra el destino para las claves externas. por supuesto, si su base de datos está bien diseñada, los objetivos para claves externas son claves primarias. Observe la edición en la parte superior que sugiere cómo se podría cambiar para encontrar dónde no está indexada la * fuente * de una clave externa (que en realidad no es demasiado útil en mi humilde opinión). Perdón por la confusión, probablemente la respuesta deba ser reescrita. – araqnid

5

La información está dentro de la catalog tables. Pero parece que no es muy sencillo querer lo que necesita, especialmente si ya hay algunos índices creados (y qué ocurre con los índices de varias columnas ...)

Si no tiene ningún FK indexado, podría hacer algo rápido y sucio, como

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

se podría reemplazar con el esquema que está interesado, que volcar a un archivo, editar, revisar, rezar y alimentar a psql. TEN CUIDADO con este procedimiento no detecta índices ya existentes.

Ah, foreign_key_tables es una vista informativo creada como:

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

Esto parece ser más de lo que estoy buscando. Tristemente, los nombres de mi tabla/columna se hacen demasiado largos para ejecutarlo directamente. Déjame jugar con eso un poco. – biggusjimmus

+0

¡buena solución por leonbloy !. Esto es lo que estoy buscando porque no puedo crear esos índices en el archivo de mapeo nhibernate (hbm). Mi última solución sería ejecutar script sql. –

0

he creado un script con este código, parece que hay un poco más corto:

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

comentario en la última línea, si usted no desea volver a crear ya los índices existentes

Cuestiones relacionadas