ローワンのハック を行う理由 仕事(ほとんど)?
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ステートメントを解析し、 anyの場合は例外をスローします 関係する列のは存在しません。
秘訣は、問題の列名と同じ名前のテーブル名(またはエイリアス)を導入することです。 extra
この場合。すべてのテーブル名を全体として参照できるため、行全体がタイプ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;
利便性
You could encapsulate the test for existence in a simple SQL function (once), arriving (almost) at the function you have been asking for:
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);
関数を使用すると高速であることが判明したため、ここで追加の関係を持つフォームを使用します。
それでも、テキスト表現しか取得できません これらのクエリのいずれかを含む列の。 実際のタイプを取得するのはそれほど簡単ではありません 。
ベンチマーク
9.1ページと9.2ページで100,000行の簡単なベンチマークを実行して、これらが最速であることを確認しました。
最速:
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 こちら
古い