31

次の式を含む PostgresSELECTステートメントがあります。

,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_idtrue と評価されるかどうかによって出力が異なる 5 つのフィールドがあります。CASE同じ条件の発言を何度も繰り返しています。

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

4

2 に答える 2

37

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;

派生テーブルx1行なので、特に条件をつけずに結合しても問題ありません。

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

条件が でない場合TRUE、 のすべての値xは NULL であり、COALESCE開始されます。

またはrtd2、特定のケースではすべての候補値がテーブルから取得されるため、元の条件LEFT JOINを使用してデフォルト値を持つ行に移動します。rtd2CASECROSS 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. 行型を分解する

カスタム複合型 (行型) を使用して、さまざまな型の値を保持し、それを単に *-outer で展開できSELECTます。textintegerおよびの 3 つの列があるとしdateます。繰り返し使用するには、カスタム複合型を作成します。

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

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

または、タイプが一時的にのみ必要な場合は、セッション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 回評価されます。評価が自明でない場合は、サブクエリを使用した他のバリアントの方が高速です。

于 2012-12-05T00:03:47.760 に答える
1

それが改善されるかどうかはわかりませんが、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周りに を適用できます。

于 2012-12-04T23:11:29.480 に答える