web-dev-qa-db-ja.com

PostgreSQLでCASEを使用して複数の列に一度に影響を与える

これらの式を持つPostgres SELECTステートメントがあります。

,CASE WHEN (rtp.team_id = rtp.sub_team_id)
 THEN 'testing'
 ELSE TRIM(rtd2.team_name)
 END AS testing_testing
,CASE WHEN (rtp.team_id = rtp.sub_team_id)
 THEN 'test example'
 ELSE TRIM(rtd2.normal_data)
 END AS test_response
,CASE WHEN (rtp.team_id = rtp.sub_team_id)
 THEN 'test example #2'
 ELSE TRIM(rtd2.normal_data_2)
 END AS another_example

私の特定のクエリには、出力がrtp.team_id = rtp.sub_team_idはtrueと評価されます。同じ条件でCASEステートメントを何度も繰り返しています。

これらのCASE式を組み合わせて、複数の列の出力を一度に切り替える方法はありますか?

26
Elliot B.

1.標準SQL:LEFT JOIN値の単一行

LEFT JOIN 条件を使用して値の行を指定できます(これにより、一度評価します)。次に、 COALESCE() を使用して、列ごとにフォールバック値を追加できます。

この構文のバリエーションは、複数の値を使用すると短く、わずかに速くなります-特に高価な/長い条件で興味深い:

SELECT COALESCE(x.txt1, trim(r2.team_name))     AS testing_testing
     , COALESCE(x.txt2, trim(r2.normal_data))   AS test_response
     , COALESCE(x.txt3, trim(r2.normal_data_2)) AS another_example
FROM   rtp
JOIN   rtd2 r2 ON <unknown condition> -- missing context in question
LEFT   JOIN (
   SELECT 'testing'::text         AS txt1
        , 'test example'::text    AS txt2
        , 'test example #2'::text AS txt3
   ) x ON rtp.team_id = rtp.sub_team_id;

派生テーブルxsingle行で構成されるため、それ以上の条件なしで結合しても問題ありません。

明示的な型キャストは、サブクエリで必要です。例ではtextを使用しています(これは文字列リテラルのデフォルトです)。実際のデータ型を使用してください。構文ショートカットvalue::typeはPostgres固有であり、標準SQLにはcast(value AS type)を使用します。

条件がTRUEでない場合、xのすべての値はNULLであり、COALESCEが有効になります。

または、特定のケースではすべての候補値がテーブルrtd2から取得されるため、元のCASE条件を使用してLEFT JOINからrtd2に、デフォルト値を持つ行にCROSS JOINを使用します。

SELECT COALESCE(trim(r2.team_name),     x.txt1) AS testing_testing
     , COALESCE(trim(r2.normal_data),   x.txt2) AS test_response
     , COALESCE(trim(r2.normal_data_2), x.txt3) AS another_example
FROM   rtp
LEFT   JOIN rtd2 r2 ON <unknown condition>  -- missing context in question
                   AND rtp.team_id = rtp.sub_team_id
CROSS  JOIN (
   SELECT 'testing'::text         AS txt1
        , 'test example'::text    AS txt2
        , 'test example #2'::text AS txt3
   ) x;

結合条件とクエリの残りの部分に依存します。

2. PostgreSQL固有

2a。配列を展開する

さまざまな列が同じデータ型を共有している場合、サブクエリで配列を使用し、外側のSELECTで展開できます。 :

SELECT x.combo[1], x.combo[2], x.combo[3]
FROM  (
   SELECT CASE WHEN rtp.team_id = rtp.sub_team_id
            THEN '{test1,test2,test3}'::text[]
            ELSE ARRAY[trim(r2.team_name)
                     , trim(r2.normal_data)
                     , trim(r2.normal_data_2)]
          END AS combo
   FROM   rtp
   JOIN   rtd2 r2 ON <unknown condition>
   ) x;

列が同じデータ型を共有しない場合、より複雑になります。それらをすべてtextにキャストする(またはオプションで外側のSELECTに戻す)か、または...

2b。行タイプを分解する

カスタム複合型(行型)を使用して、さまざまな型の値を保持し、外側のSELECTで単純に* -expandできます。 textintegerdateの3つの列があるとします。 repeatedを使用するには、カスタムの複合型を作成します。

CREATE TYPE my_type (t1 text, t2 int, t3 date);

または既存のテーブルのタイプが一致する場合、テーブル名を複合タイプとして使用できます。

またはタイプtemporarilyのみが必要な場合は、TEMPORARY TABLEを作成して、一時的なタイプをセッション

CREATE TEMP TABLE my_type (t1 text, t2 int, t3 date);

単一トランザクションに対してもこれを行うことができます。

CREATE TEMP TABLE my_type (t1 text, t2 int, t3 date) ON COMMIT DROP;

次に、このクエリを使用できます。

SELECT (x.combo).*  -- parenthesis required
FROM  (
   SELECT CASE WHEN rtp.team_id = rtp.sub_team_id
             THEN ('test', 3, now()::date)::my_type  -- example values
             ELSE (r2.team_name
                 , r2.int_col
                 , r2.date_col)::my_type
          END AS combo
   FROM   rtp
   JOIN   rtd2 r2 ON <unknown condition>
   ) x;

または単に(上記と同じ、より単純、より短く、おそらく理解しにくい):

SELECT (CASE WHEN rtp.team_id = rtp.sub_team_id
           THEN ('test', 3, now()::date)::my_type
           ELSE (r2.team_name, r2.int_col, r2.date_col)::my_type
        END).*
FROM   rtp
JOIN   rtd2 r2 ON <unknown condition>;

CASE式は、この方法ですべての列に対して1回評価されます。評価が簡単でない場合、サブクエリを持つ他のバリアントはより高速になります。

30

改善されるかどうかはわかりませんが、SELECTをそれ自体と別の方法で結合できます。

SELECT 
  ...,
  'testing' AS testing_testing,
  'test example' AS test_response,
  'test example #2' AS another_example, ...
FROM ...
WHERE rtp.team_id = rtp.sub_team_id AND ...
UNION 
SELECT
  ...,
  TRIM(rtd2.team_name) AS testing_testing,
  TRIM(rtd2.normal_data) AS test_response,
  TRIM(rtd2.normal_data_2) AS another_example, ...
WHERE rtp.team_id <> rtp.sub_team_id AND ...;

列名は、最初のクエリと同じ順序で取り出すと仮定して、2番目のクエリから安全に省略できます。

共通テーブル式(CTE)を使用して、それぞれを個別のクエリにすることができます。この順序の変更が心配な場合は、サブクエリにして、ORDER BY その周りに。

1
Daniel Lyons