2

既存の PostgreSQL 9.1 データベースの構造を読み取り、それを「あるべき」状態と比較し、それに応じてデータベースを更新するアプリケーションがあります。ほとんどの場合、それはうまくいきます。ただし、現在のデータベース構造を読み取るときにデッドロックが発生した場合がいくつかありました。担当するクエリは、既存の外部キーを読み取ります。

SELECT tc.table_schema, tc.table_name, tc.constraint_name, kcu.column_name,
       ccu.table_schema, ccu.table_name, ccu.column_name
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
     ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
     ON ccu.constraint_name = tc.constraint_name
WHERE constraint_type = 'FOREIGN KEY'

pgAdmin でサーバーのステータスを表示すると、これがサーバー上で実行されている唯一のアクティブなクエリ/トランザクションであることがわかります。それでも、クエリは返されません。

エラーはある意味で再現可能です。エラーを生成するデータベースを見つけると、毎回エラーが生成されます。ただし、すべてのデータベースでエラーが発生するわけではありません。これは不可解なバグの 1 つであり、他に何を試すか、またはこれを回避する方法についてのオプションとアイデアが不足しています。そのため、ご意見やアイデアは大歓迎です。

PS: 私の同僚が、PostgreSQL 8.4 を使用して同じエラーが発生したと報告しました。

4

1 に答える 1

1

テストしたところ、クエリも非常に遅いことがわかりました。この問題の根源は、「テーブル」information_schemaが、実際には、SQL 標準に従ってカタログを提供するための複雑なビューであることです。この特定のケースでは、外部キーが複数の列に構築される可能性があるため、問題はさらに複雑になります。あなたのクエリは、望ましくない副作用であると思われる場合に重複した行を生成します。

これは、以下のクエリでサブクエリ構造unnestを使用する理由でもあります。ARRAY

この代替クエリを検討してください。重複する行がなく、100 倍高速なだけで、同じ情報が得られます。また、デッドロックなしで、あえて保証します。

もちろん、このクエリは PostgreSQL でのみ機能し、他の RDBMS には移植できません。

SELECT c.conrelid::regclass AS table_name
      ,c.conname AS fk_name
      ,ARRAY(SELECT a.attname
             FROM   unnest(c.conkey) x
             JOIN   pg_attribute a
             ON     a.attrelid = c.conrelid AND a.attnum = x) AS fk_columns
      ,c.confrelid::regclass AS ref_table
      ,ARRAY(SELECT a.attname
             FROM   unnest(c.confkey) x
             JOIN   pg_attribute a
             ON     a.attrelid = c.confrelid AND a.attnum = x) AS ref_columns
FROM   pg_catalog.pg_constraint c
WHERE  c.contype = 'f';
-- ORDER  BY c.conrelid::regclass::text,2

私は特別な鋳造操作table_oid::regclassを使用します。これにより、現在アクティブな で見られるテーブル名が生成されますsearch_path。それはあなたが望むものかもしれませんし、そうでないかもしれません。このクエリにすべてのテーブル名の絶対パス (スキーマ) を含めるには、次のように設定search_pathできます。

SET search_path = pg_catalog;
SELECT ...

残りはご存知かと思いますが、一般の方向けに載せておきます。
このセッションを続行し、デフォルトの search_path に戻したい場合は、次のようにします。

RESET search_path;

カスタムを使用する必要がある場合はsearch_path、再度設定する必要があります。

于 2011-10-25T06:11:25.713 に答える