查询性能不佳PostgreSQL

时间:2016-04-07 21:46:06

标签: sql postgresql query-performance

我有一个问题:

SELECT
        '{\"nombre\":\"'||c.column_name||'\",\"type\":\"'|| c.data_type
        ||'\",\"is_nullable\":\"'|| c.is_nullable||'\",\"is_pk\":\"'|| CASE WHEN constraint_type = 'PRIMARY KEY' THEN 'SI' ELSE 'NO' END
        ||'\",\"max_length\":\"'||COALESCE(c.character_maximum_length::VARCHAR,'')||'\",\"FK_schema\":\"'||COALESCE(ccu.table_schema,'')||'\",\"FK_tabla\":\"'||
       COALESCE(ccu.table_name,'')||'\",\"FK_columna\":\"'||COALESCE(ccu.column_name,'')||
       '\"}' as id, c.column_name
FROM information_schema.columns AS c
LEFT JOIN  information_schema.key_column_usage i ON I.table_name=c.table_name AND I.table_schema=c.table_schema AND c.column_name = I.column_name 
LEFT JOIN information_schema.table_constraints tc ON TC.constraint_name = I.CONSTRAINT_NAME AND constraint_type IN ('FOREIGN KEY','PRIMARY KEY')
LEFT JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name 
AND constraint_type IN ('FOREIGN KEY')

WHERE c.table_schema = '".$_POST['schema']."' AND c.table_name='".$_POST['tabla']."'
AND NOT EXISTS (
        SELECT q.column_name FROM information_schema.constraint_column_usage  q
        inner join information_schema.table_constraints USING(constraint_name )
        WHERE c.table_schema = q.table_schema AND q.table_name=c.table_name AND q.column_name = c.column_name 
        AND tc.constraint_name > q.constraint_name AND TC.constraint_type <> 'PRIMARY KEY') 
ORDER BY c.ordinal_position;

它可以工作,但在具有97个模式的数据库中,运行需要一分钟。

我该如何解决这个问题?

没有这一行:

LEFT JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name AND constraint_type = 'FOREIGN KEY'

只需不到1秒钟。

2 个答案:

答案 0 :(得分:2)

由于查询全部似乎都是关于数据库的 Postgres 元数据,因此可能不会像查询中的常规表组那样经常更改。

因此,我建议您使用materialized view设置为在您认为可接受的数据货币的某个时间间隔内刷新。也许这是每小时,或每天 - 这完全取决于这些数据的变化频率,以及您需要它的最新频率(也是相互关联的)。

所以你会这样做:

CREATE MATERIALIZED VIEW mat_view AS
SELECT
        '{\"nombre\":\"'||c.column_name||'\",\"type\":\"'|| c.data_type
        ||'\",\"is_nullable\":\"'|| c.is_nullable||'\",\"is_pk\":\"'|| CASE WHEN constraint_type = 'PRIMARY KEY' THEN 'SI' ELSE 'NO' END
        ||'\",\"max_length\":\"'||COALESCE(c.character_maximum_length::VARCHAR,'')||'\",\"FK_schema\":\"'||COALESCE(ccu.table_schema,'')||'\",\"FK_tabla\":\"'||
       COALESCE(ccu.table_name,'')||'\",\"FK_columna\":\"'||COALESCE(ccu.column_name,'')||
       '\"}' as id, c.column_name
FROM information_schema.columns AS c
LEFT JOIN  information_schema.key_column_usage i ON I.table_name=c.table_name AND I.table_schema=c.table_schema AND c.column_name = I.column_name 
LEFT JOIN information_schema.table_constraints tc ON TC.constraint_name = I.CONSTRAINT_NAME AND constraint_type IN ('FOREIGN KEY','PRIMARY KEY')
LEFT JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name 
AND constraint_type IN ('FOREIGN KEY')

WHERE c.table_schema = '".$_POST['schema']."' AND c.table_name='".$_POST['tabla']."'
AND NOT EXISTS (
        SELECT q.column_name FROM information_schema.constraint_column_usage  q
        inner join information_schema.table_constraints USING(constraint_name )
        WHERE c.table_schema = q.table_schema AND q.table_name=c.table_name AND q.column_name = c.column_name 
        AND tc.constraint_name > q.constraint_name AND TC.constraint_type <> 'PRIMARY KEY') 
ORDER BY c.ordinal_position;

这将基本上创建该视图首次运行时的结果快照。然后可以通过REFRESH MATERIALIZED VIEW mat_view;

刷新

您可能还希望使用CONCURRENTLY选项,这可以防止阻止,但可能需要更长时间。

可以将REFRESH MATERIALIZED VIEW命令放在cron上,以确保它按照您希望的时间间隔完成。

注意:实体化视图是 Postgres 9.3中的新功能。 {em> Postgres 9.4中添加了CONCURRENTLY

根据OP的评论进行编辑:

关于 Postgres 8.4 ,听起来似乎不在您的控制之下,但您可能想要使用此信息向服务器管理员和DBA游说,看看它是否改变了主意:< / p>

  1. Postgres 8.4大约7年前(2009年7月)发布
  2. Postgres 8.4差不多2年前(2014年7月)EOL(即不支持,不再更新)。
  3. 至于你在8.4上可以做些什么,它肯定会受到更多的限制......你可以试试的一件事可能会给你一些改进(虽然不如物化视图可能在以后的版本中)试图将JOIN分解为几个不同的临时表。这可能允许查询规划器更好地推理每个单独的查询并且比整体更快地处理部件,尽管它肯定是一个高度可变的东西,取决于数据,服务器调整和其他因素。

    另外,请尝试在查询前NOT EXISTS内的WHERE表达式中为子选择创建临时表,然后使用该表。

    确保它(以及其他临时表)具有您可能需要的任何索引,以便它可以尽可能多地执行索引扫描而不是完整索引扫描。请参阅EXPLAIN语句( PgAdmin 提供了一个可视化的语句),以帮助查看查询计划程序对查询启动的看法以及在将其分解为组件时如何更改。

答案 1 :(得分:1)

我做了一个新的查询,阅读这个postgressql ...

    select 
        '{\"nombre\":\"'||columna.attname||'\",\"tipo\":\"'|| tipo.typname
        ||'\",\"is_nullable\":\"'|| CASE WHEN columna.attnotnull THEN 'NO' ELSE 'SI' END||'\",\"is_pk\":\"'|| CASE WHEN pk.conname IS NULL THEN 'NO' ELSE 'SI' END 
        ||'\",\"max_length\":\"'||COALESCE(character_maximum_length::varchar,'')||'\",\"FK_schema\":\"'||COALESCE(fk_schema.nspname,'')||'\",\"FK_tabla\":\"'||
       COALESCE(fk_tabla.relname,'')||'\",\"FK_columna\":\"'||COALESCE(fk_columna.attname,'')||
       '\"}' as id, columna.attname,columna.*
from pg_catalog.pg_namespace pgschema
inner join pg_catalog.pg_class clase  ON PGSCHEMA.OID = clase.relnamespace and relname ='".$_POST['tabla']."'
inner join  pg_catalog.pg_attribute columna ON ATtReLID = clase.oid
inner join pg_catalog.pg_type tipo ON tipo.oid = columna.atttypid
inner join information_schema.columns ON table_schema = pgschema.nspname AND table_name = clase.relname AND column_name = columna.attname
left join pg_catalog.pg_constraint pk ON pk.contype = 'p' AND PK.conrelid = clase.oid and array[columna.attnum] && pk.conkey
left join pg_catalog.pg_constraint Fk ON FK.contype = 'f' AND FK.conrelid = clase.oid and array[columna.attnum] && fk.conkey
left join pg_catalog.pg_class Fk_tabla ON FK.confrelid = fk_tabla.oid -- AND FK.conrelid = clase.oid and array[columna.attnum] && fk.conkey
left join pg_catalog.pg_namespace Fk_schema ON FK_tabla.relnamespace = fk_schema.oid
left join  pg_catalog.pg_attribute fk_columna ON fk_tabla.OID = fk_columna.attrelid AND ARRAY[fk_columna.attnum] && fk.confkey
where pgschema.nspname = '".$_POST['schema']."' and columna.attnum > 0;

只需不到1秒钟。