SELECT id, title
, CASE WHEN extra_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN (
SELECT EXISTS (
SELECT FROM information_schema.columns
WHERE table_name = 'tbl'
AND column_name = 'extra')
) AS extra(extra_exists)
通常、それはまったく機能しません。関連する列が存在しない場合、Postgres は SQL ステートメントを解析し、例外をスローします。
トリックは、問題の列名と同じ名前のテーブル名 (またはエイリアス) を導入することです。extra
この場合。すべてのテーブル名は全体として参照でき、その結果、行全体が type として返されrecord
ます。また、すべての型を にキャストできるためtext
、このレコード全体を にキャストできますtext
。このようにして、Postgres はクエリを有効なものとして受け入れます。
列名はテーブル名よりも優先されるため、列が存在する場合は列でextra::text
あると解釈されますtbl.extra
。そうしないと、デフォルトでテーブルの行全体が返されますが、extra
これは決して起こりません。
自分の目で確かめるために、別のテーブル エイリアスを選択してみてextra
ください。
これは文書化されていないハックであり、Postgres が将来のバージョンで SQL 文字列を解析および計画する方法を変更することを決定した場合、壊れる可能性があります。
明確な
これを使用する場合は、少なくとも明確にしてください。
テーブル名だけでは一意ではありません。「tbl」という名前のテーブルは、同じデータベースの複数のスキーマに何度でも存在する可能性があり、非常に紛らわしく完全に誤った結果になる可能性があります。さらにスキーマ名を指定する必要があります。
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN (
SELECT EXISTS (
SELECT FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'tbl'
AND column_name = 'extra'
) AS col_exists
) extra;
もっと早く
このクエリは他の RDBMS にほとんど移植できないため、情報スキーマ ビューの代わりにカタログ テーブルpg_attribute
information_schema.columns
を使用することをお勧めします。約10倍高速。
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN (
SELECT EXISTS (
SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = 'myschema.tbl'::regclass -- schema-qualified!
AND attname = 'extra'
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0 -- no system columns
)
) extra(col_exists);
また、より便利で安全な へのキャストをregclass
使用します。見る:
必要なエイリアスをアタッチして、プライマリ テーブル自体を含む任意のテーブルに Postgres をだますことができます。別のリレーションに参加する必要はまったくありません。これが最も速いはずです。
SELECT id, title
, CASE WHEN EXISTS (SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = 'tbl'::regclass
AND attname = 'extra'
AND NOT attisdropped
AND attnum > 0)
THEN extra::text
ELSE 'default' END AS extra
FROM tbl AS extra;
快適
存在するかどうかのテストを単純な SQL 関数 (1 回) にカプセル化し、求めていた関数に (ほぼ) 到達することができます。
CREATE OR REPLACE FUNCTION col_exists(_tbl regclass, _col text)
RETURNS bool
LANGUAGE sql STABLE AS
$func$
SELECT EXISTS (
SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = $1
AND attname = $2
AND NOT attisdropped
AND attnum > 0
)
$func$;
COMMENT ON FUNCTION col_exists(regclass, text) IS
'Test for existence of a column. Returns TRUE / FALSE.
$1 .. exact table name (case sensitive!), optionally schema-qualified
$2 .. exact column name (case sensitive!)';
クエリを次のように単純化します。
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN col_exists('tbl', 'extra') AS extra(col_exists);
関数を使用するとより高速であることが判明したため、ここで追加のリレーションを使用してフォームを使用します。
それでも、これらのクエリでは、列のテキスト表現しか得られません。実際の typeを取得するのは簡単ではありません。
基準
pg 9.1 と 9.2 で 100k 行の簡単なベンチマークを実行して、これらが最速であることを確認しました。
最速:
SELECT id, title
, CASE WHEN EXISTS (SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = 'tbl'::regclass
AND attname = 'extra'
AND NOT attisdropped
AND attnum > 0)
THEN extra::text
ELSE 'default' END AS extra
FROM tbl AS extra;
2 番目に速い:
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN col_exists('tbl', 'extra') AS extra(col_exists);
ここでdb<>fiddle
古いsqlfiddle