我有一个带有这些表达式的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
表达式来一次切换多列的输出?
答案 0 :(得分:28)
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
会启动。
或,因为在您的特定情况下,所有候选值都来自表rtd2
,LEFT JOIN
到rtd2
使用原始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;
这取决于连接条件和查询的其余部分。
如果您的各列共享 相同的数据类型 ,您可以在子查询中使用数组并在外部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
),或者您可以...
您可以使用自定义复合类型(行类型)来保存各种类型的值,只需在外部SELECT
中*展开它。假设我们有三列:text
,integer
和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 :(得分: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
。