31

我有一个SELECT带有这些表达式的 Postgres 语句:

,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表达式来一次切换多列的输出?

4

2 回答 2

37

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 特有的,cast(value AS type)用于标准 SQL。

如果条件不是TRUE,则所有值x都为 NULL,并COALESCE开始执行。

或者rtd2,由于在您的特定情况下所有候选值都来自表LEFT JOINrtd2因此使用原始CASE条件和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。分解行类型

您可以使用自定义复合类型(行类型)来保存各种类型的值,并简单地将其 *-expand 到 external 中SELECT。假设我们有三列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方式对每一列计算一次表达式。如果评估不是微不足道的,那么带有子查询的另一个变体会更快。

于 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 ...;

列名可以安全地从第二个查询中省略,假设您以与第一个相同的顺序将它们取出。

您可能希望使用公用表表达式 (CTE) 将其中的每一个都设为单独的查询。如果您担心这会更改顺序,可以将其设为子查询并ORDER BY在其周围应用。

于 2012-12-04T23:11:29.480 回答