回答

收藏

在PostgreSQL中使用CASE一次影响多列

技术问答 技术问答 116 人阅读 | 0 人回复 | 2023-09-14

我有一个SELECT有这些表达式Postgres语句:
' ]/ }" c. O1 }# |& S3 f0 ~- ~,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一遍又一遍地重复具有相同条件的句子。0 C2 [7 p  ^' _8 b: B
有什么方法可以组合这些?CASE多列输出一次切换表达式?
3 U3 R8 a$ E8 }! {5 j                                                               
8 m: f# p, J; [" p! i8 J8 d    解决方案:                                                               
! H8 f7 Q8 r+ _* ^8 d                                                                1. Standard-SQL:LEFT JOIN一行值您可以LEFT JOIN使用条件一行值,使用条件估)获得一行值。然后,您可以使用添加每列的后备值COALESCE()。8 A$ f" D' g8 D3 K1 N, V3 Z
这种语法变体短,多个值时速度快-特别有趣的是昂贵/冗长的条件:
; y" ]4 I8 v. C6 h1 I  SSELECT 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_exampleFROM   rtpJOIN   rtd2 r2 ON <u> -- missing context in questionLEFT   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由 单行    组成,无需其他条件即可连接。
1 M1 W8 z% w4 o* |( `9 v3 ^__ 必须在子查询中进行转换显式类型
; X! m: u- _3 h& n* G; V0 a。我text在示例中使用(无论如何都是字符串文本的默认设置)。使用您的实际数据类型。语法快捷value::type是Postgres特定的,cast(valueAS type)用于标准SQL。' c- I8 n  \$ h0 x0 n
若条件不是TRUE,则inx所有值都是NULL,并COALESCE加入。
# V+ ?, F- r2 b; B' j或者    ,因为所有的候选值都来自表rtd你的具体情况,LEFT JOIN来rtd2使用原来的CASE状态,并CROSSJOIN默认值:* k% k9 {3 @3 o( H; a4 h1 r0 t
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_exampleFROM   rtpLEFT   JOIN rtd2 r2 ON <u>  -- missing context in question                   AND rtp.team_id = rtp.sub_team_idCROSS  JOIN (   SELECT 'testing'::text         AS txt1     'test example'::text    AS txt2     'test example #2'::text AS txt3   ) x;它取决于连接条件和查询的其他部分。
; K* t$ |2 A" R/ T. ^  `0 B' h2.特定于PostgreSQL2a。展开数组如果您的列共享 _ 数据类型相同_ ,可在子查询中使用数组,然后在外部展开SELECT:
- k' m" b1 A4 p6 {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 <u>   ) x;如果列不共享相同的数据类型,它将变得更加复杂。你可以把它们都做text转换为(并且可以选择在外部转换SELECT),也可以…5 s; X5 J/ r; R: ~( d% S  {+ n- K
2b。分解行类型您可以使用自定义的复合类型(行类型)来保存各种类型的值,并且可以简单地在外部*扩展它SELECT。假设我们有三列:text,integer和date。要! z" m5 M; W( z# v% G
重复    使用时,请创建自定义复合类型:
$ x8 X. C& l* ~5 E9 K# _CREATE TYPE my_type (t1 text,t2 int,t3 date);或者,    如果现有表的类型匹配,只能使用表名作为复合类型。9 O; \+ V2 ~4 c* p$ D: y* Z
或者,    如果你只是 临时    如果你需要这种类型,你可以创建一个TEMPORARY TABLE,在 会话    注册临时类型:& |9 |2 Y* a4 T9 f  t  H6 J
CREATE TEMP TABLE my_type (t1 text,t2 int,t3 date);甚至可以为 单个事务    执行此操作:
. h& n' P" l; G# K0 KCREATE TEMP TABLE my_type (t1 text,t2 int,t3 date) ON COMMIT DROP;然后,您可以使用以下查询:' C. ^7 R  F/ |
SELECT (x.combo).*  -- parenthesis requiredFROM  (  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 <u>   ) x;甚至只是(和上面一样,更简单,更简短,也许不容易理解):
, q$ @+ Z/ {* X7 FSELECT (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   rtpJOIN   rtd2 r2 ON <u>;该CASE表达式一次,每列评估。如果求值不平凡,其他带子查询的变体会更快。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则