sql >> データベース >  >> RDS >> PostgreSQL

列が存在しない場合、Postgresはデフォルト値を返します

    ローワンのハック を行う理由 仕事(ほとんど)?

    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::texttbl.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 こちら
    古い sqlfiddle



    1. MYSQL往復データを選択しますか?

    2. postgresqlへのdictオブジェクトの追加

    3. FROM キーワードが予期された場所に見つかりませんでした。テキスト選択 Oracle SQL

    4. PHPとMySQL:返された行数