簡體   English   中英

在 PostgreSQL 中使用 CASE 一次影響多個列

[英]Using CASE in PostgreSQL to affect multiple columns at once

我有一個帶有這些表達式的 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

在我的特定查詢中,有 5 個字段的輸出取決於rtp.team_id = rtp.sub_team_id是否為真。 我一遍又一遍地重復具有相同條件的CASE語句。

有什么方法可以組合這些CASE表達式來一次性切換多列的輸出?

1. Standard-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;

由於派生表x單行組成,因此無需其他條件就可以進行連接。

子查詢中需要顯式類型轉換 我在示例中使用text (無論如何這是字符串文字的默認值)。 使用您的實際數據類型。 語法快捷方式value::type是 Postgres 特定的,對標准 SQL 使用cast(value AS type)

如果條件不是TRUE ,則x中的所有值都是 NULL,並且COALESCE開始。

或者,由於在您的特定情況下所有候選值都來自表rtd2rtd2使用原始CASE條件將LEFT JOINrtd2 ,並使用默認值將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 為了重復使用,創建一個自定義的復合類型:

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表達式以這種方式為每一列計算一次。 如果評估不是微不足道的,帶有子查詢的另一個變體會更快。

不確定這是否會有所改進,但您可以以另一種方式將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 ...;

列名可以安全地從第二個查詢中省略,假設您以與第一個相同的順序顯示它們。

您可能希望使用公共表表達式 (CTE) 將其中的每一個作為單獨的查詢。 如果您擔心這會更改順序,您可以將其設為子查詢並在其周圍應用ORDER BY

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM