PostgreSQL 进阶教程 1-1 CASE表达式

[toc]

第1章 神奇的SQL

1-1 CASE表达式

1-1.1 CASE表达式

简单CASE表达式

CASE sex
    WHEN '1' THEN '男',
    WHEN '2'THEN '女'
    ELSE '其他' 
    END

搜索CASE表达式

CASE WHEN sex ='1' THEN '男'
     WHEN sex ='2' THEN '女'
     ELSE '其他'
     END

两者的执行结果是相同的。简单CASE表达式正如其名,能实现的事比较简单。
简单CASE能做到的事情,搜索CASE表达式也能做到,所以尽量用搜索CASE表达式来写。

注意事项:

  1. 统一各分支返回的数据类型(不要出现某分支返回数值,另一分支返回字符串)
  2. 不要忘了写 END (绝大部分未写END导致的错误)
  3. 养成写 ELSE 子句的习惯(如果不写ELSE,返回值为NULL,如果语法有错,不易查找原因。)
将已有编号方式转换为新的方式并统计

例如有一张表,来统计北京不同地区的人口数量,想要将要以不同郊区的人口数量来统计。

在这里插入图片描述

首先按照Code1.01进行数据环境

Code 1.01

CREATE TABLE code1_01
(area VARCHAR(32) NOT NULL,
population INTEGER NOT NULL);

INSERT INTO code1_01 VALUES 
('东城',25000),('西城',35000),('昌平',6000),('大兴',5000),('怀柔',1000),('密云',1400);

用简单CASE表达式:
Code 1.02

SELECT CASE area
    WHEN  '东城' THEN '市区'
    WHEN  '西城' THEN '市区'
    WHEN  '昌平' THEN '近郊'
    WHEN  '大兴' THEN '近郊'
    WHEN  '怀柔' THEN '远郊'
    WHEN  '密云' THEN '远郊'
    ELSE '其他' 
    END AS area,
    SUM(population)
FROM code1_01
GROUP BY  CASE area
    WHEN  '东城' THEN '市区'
    WHEN  '西城' THEN '市区'
    WHEN  '昌平' THEN '近郊'
    WHEN  '大兴' THEN '近郊'
    WHEN  '怀柔' THEN '远郊'
    WHEN  '密云' THEN '远郊'
    ELSE '其他' 
    END
;

用搜索CASE表达式

Code1.03

SELECT CASE 
    WHEN area = '东城'THEN '市区'
    WHEN area = '西城'THEN '市区'
    WHEN area = '昌平'THEN '近郊'
    WHEN area = '大兴'THEN '近郊'
    WHEN area = '怀柔'THEN '远郊'
    WHEN area = '密云'THEN '远郊'
    ELSE '其他' END AS area,
    SUM(population)
FROM code1_01
GROUP BY CASE 
    WHEN area = '东城'THEN '市区'
    WHEN area = '西城'THEN '市区'
    WHEN area = '昌平'THEN '近郊'
    WHEN area = '大兴'THEN '近郊'
    WHEN area = '怀柔'THEN '远郊'
    WHEN area = '密云'THEN '远郊'
ELSE '其他' END;

进一步简化
Code 1.04

SELECT CASE 
    WHEN area IN ('东城','西城')THEN '市区'
    WHEN area IN ('昌平','大兴')THEN '近郊'
    WHEN area IN ('怀柔','密云')THEN '远郊'
    ELSE '其他' END AS area,
    SUM(population)
FROM code1_01
GROUP BY CASE 
    WHEN area IN ('东城','西城')THEN '市区'
    WHEN area IN ('昌平','大兴')THEN '近郊'
    WHEN area IN ('怀柔','密云')THEN '远郊'
ELSE '其他' END;

如果想要得到如下的结果
在这里插入图片描述

Code 1.05

SELECT CASE 
    WHEN population <=5000 THEN  '小型城市'
    WHEN population >5000 AND population<=10000  THEN '中型城市'
    WHEN population >10000  THEN '大型城市'
    ELSE '其他' END AS "城市等级",
count(area) AS "数量"
FROM code1_01
GROUP BY CASE 
    WHEN population <=5000 THEN  '小型城市'
    WHEN population >5000 AND population<=10000  THEN '中型城市'
    WHEN population >10000  THEN '大型城市'
    ELSE '其他' END;

这个方法也很好,但是SELECT和GROUP BY需要写两条一模一样的CASE函数,后期修改容易出错。

但是对于某些关系型数据库(PostgreSQL/MySql)来说可以采取以下写法。

Code 1.06

SELECT CASE 
    WHEN population <=5000 THEN  '小型城市'
    WHEN population >5000 AND population<=10000  THEN '中型城市'
    WHEN population >10000  THEN '大型城市'
    ELSE '其他' END AS "城市等级",
count(area) AS "数量"
FROM code1_01
GROUP BY "城市等级";

正常情况下GROUP BY 语句比SELECT先执行,所以GROUP BY引用SELECT的别名是不符合规定的。
而PostgreSQL/MySql数据库中,在执行GROUP BY 语句中,先会在SELECT进行扫描(不计算),所以在这些数据库中执行,不会报错。
但是不符合SQL规定,所以不建议采取这种方式,但是会提高语句的可读性。

用一条SQL语句进行不同条件的统计

例如如下图,左边的表为数据,想要达到右边的效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dULQa50R-1673180083510)(image/02笔记SQL进阶教程作者MICK/1673163916106.png)]

Code 1.07 创建数据环境

CREATE TABLE poptbl2 
(area VARCHAR(32) NOT NULL,sex char(2) NOT NULL,population INTEGER NOt NULL);

INSERT INTO poptbl2 VALUES 
('东城','1',55),
('东城','2',65),
('西城','1',85),
('西城','2',95),
('海淀','1',78),
('海淀','2',91),
('丰台','1',86),
('丰台','2',71),
('昌平','1',105),
('昌平','2',115),
('房山','1',95),
('房山','2',87),
('密云','1',93),
('密云','2',88),
('平谷','1',92),
('平谷','2',94);

通常做法是利用WHERE语句,形成两个SELECT,并进行UNION。

Code 1.08 利用WHERE

SELECT area,SUM(population) FROM poptbl2 WHERE sex='1' GROUP BY area
UNION
SELECT area,SUM(population) FROM poptbl2 WHERE sex='2' GROUP BY area;

在这里插入图片描述

用这种方法,工作量并没有减少,而且SQL语句也变的很长,而如果用CASE表达式,可以用一条简单的SQL语句搞定。

Code 1.09

SUM(CASE WHEN sex='2' THEN population ELSE 0 END) AS cnt_m,
SUM(CASE WHEN sex='1' THEN population ELSE 0 END) AS cnt_f
FROM poptbl2
GROUP BY area;

在这里插入图片描述

<think>嗯,用户遇到了一个在 PostgreSQL 中的语法错误,错误信息是“syntax error at or near ‘case’”。我的任务就是帮他解决这个问题。首先,我需要回想一下用户可能在哪里使用了CASE语句导致这个错误。 可能的情况有很多种,我需要逐一分析。首先,用户可能是在写SQL查询或者PL/pgSQL函数时,错误地使用了CASE语句的结构。常见的错误包括拼写错误,比如把CASE写成小写或者拼错,不过PostgreSQL对关键字的大小写不敏感,所以这可能不是问题。但用户可能忽略了某些语法规则,比如缺少END关键字,或者在WHEN子句后没有THEN,或者在ELSE之后没有表达式。 接下来,我要考虑用户提供的上下文。之前的对话中,用户询问了如何在PL/pgSQL函数中使用IF和ELSIF语句根据不同参数赋值。现在用户遇到了关于CASE的语法错误,可能是在尝试使用CASE语句替代IF语句时出现了问题。这时候需要比较两者的语法差异,指出用户可能在转换时犯了结构错误。 另外,用户可能在动态SQL或者存储过程中错误地使用了CASE语句。例如,在PL/pgSQL中,CASE语句的结构需要严格遵循语法,每个WHEN必须有对应的THEN,并且必须以END CASE结束,而不是END。如果用户在这里写错了,就会导致语法错误。 然后,我需要检查用户可能的代码示例。比如,用户是否在CASE语句中遗漏了必要的部分,或者在不支持CASE的地方使用了它。例如,在CREATE FUNCTION语句中,或者在表达式内部是否正确使用了CASE。 可能的错误示例包括: 1. 忘记结束CASE语句: ```sql SELECT CASE WHEN score > 90 THEN 'A' WHEN score > 80 THEN 'B' -- 缺少 ELSE 和 END FROM students; ``` 2. 错误使用END CASE: ```sql CASE WHEN condition THEN result END CASE; -- 正确应为END ``` 3. 在PL/pgSQL中使用CASE语句时结构错误: ```sql CREATE FUNCTION test() RETURNS TEXT AS $$ BEGIN RETURN CASE WHEN true THEN 'Yes' -- 缺少 ELSE 或其他WHEN END; -- 这里可能需要END CASE? END; $$ LANGUAGE plpgsql; ``` 这时候需要指出,在SQL中的CASE表达式和PL/pgSQL中的CASE语句的细微差别。例如,在PL/pgSQL中,CASE语句的结构可能略有不同,或者用户混淆了两者的用法。 另外,用户可能在CASE语句中使用了不正确的比较操作符,或者条件表达式有误,导致解析器在遇到CASE时无法正确识别结构,从而抛出错误。 还需要考虑是否有保留字冲突,比如使用CASE作为列名或别名,但没有正确转义,不过这种情况较少见,因为CASE是关键字,通常需要引号括起来,但错误信息指向的是语法错误在CASE附近,所以更可能是结构问题。 最后,总结常见的错误点,并给出修正后的示例,帮助用户理解如何正确使用CASE语句,避免语法错误。同时,可能需要提醒用户注意不同上下文(如SQL查询与PL/pgSQL函数)中CASE用法的差异,以及如何正确结束CASE语句,确保每个分支都有THEN和合适的表达式,并以END结尾。</think>在 PostgreSQL 中遇到 `syntax error at or near "case"` 错误,通常是由于 **`CASE` 语句/表达式使用不规范**导致的。以下是常见原因及解决方案: --- ### 一、基础语法错误 #### 错误示例 1:缺少 `END` ```sql SELECT CASE WHEN score > 90 THEN 'A' -- 缺少 END FROM students; ``` **报错原因**:`CASE` 表达式必须以 `END` 结尾。 **修复方案**: ```sql SELECT CASE WHEN score > 90 THEN 'A' ELSE '其他' END AS grade -- 补全 END FROM students; ``` --- #### 错误示例 2:混淆 `CASE` 表达式与语句 ```sql CREATE FUNCTION test() RETURNS TEXT AS $$ BEGIN CASE -- 错误:PL/pgSQL 中需用 CASE 表达式或 IF 语句 WHEN 1=1 THEN RETURN 'Yes'; END CASE; END; $$ LANGUAGE plpgsql; ``` **报错原因**:PL/pgSQL 中 `CASE` 是表达式(返回值的),不能直接作为流程控制语句。 **修复方案**:改用 `IF` 语句或正确使用 `CASE` 表达式: ```sql -- 方案 1:用 IF 语句 CREATE FUNCTION test() RETURNS TEXT AS $$ BEGIN IF 1=1 THEN RETURN 'Yes'; END IF; END; $$ LANGUAGE plpgsql; -- 方案 2:用 CASE 表达式(需配合赋值或返回) CREATE FUNCTION test() RETURNS TEXT AS $$ BEGIN RETURN CASE WHEN 1=1 THEN 'Yes' ELSE 'No' END; END; $$ LANGUAGE plpgsql; ``` --- ### 二、进阶易错点 #### 错误示例 3:动态 SQL 中的拼接错误 ```sql CREATE FUNCTION query_data(col TEXT) RETURNS TABLE (val INT) AS $$ BEGIN RETURN QUERY EXECUTE ' SELECT CASE ' || col || ' -- 错误:动态拼接导致语法断裂 WHEN 1 THEN 100 END FROM table'; END; $$ LANGUAGE plpgsql; ``` **报错原因**:动态 SQL 拼接时破坏了 `CASE` 结构。 **修复方案**:完整构造 `CASE` 表达式: ```sql CREATE FUNCTION query_data(col TEXT) RETURNS TABLE (val INT) AS $$ BEGIN RETURN QUERY EXECUTE format(' SELECT CASE %I -- 使用 format 安全拼接 WHEN 1 THEN 100 ELSE 0 END FROM table', col); END; $$ LANGUAGE plpgsql; ``` --- #### 错误示例 4:`WHEN` 子句缺少条件 ```sql SELECT CASE WHEN THEN '无效条件' -- 缺少条件表达式 ELSE '其他' END FROM table; ``` **报错原因**:`WHEN` 后必须跟随布尔表达式。 **修复方案**: ```sql SELECT CASE WHEN column IS NULL THEN '空值' -- 补全条件 ELSE '非空' END FROM table; ``` --- ### 三、特殊场景排查 #### 场景 1:保留字冲突 ```sql SELECT case AS alias -- 错误:使用 CASE 作为别名 FROM (SELECT 1) AS case; -- 错误:用 CASE 作表别名 ``` **报错原因**:`CASE` 是关键字,不能直接作为别名或标识符。 **修复方案**:用双引号转义: ```sql SELECT "case" AS "alias" -- 正确转义 FROM (SELECT 1) AS "case"; ``` --- #### 场景 2:嵌套 `CASE` 结构混乱 ```sql SELECT CASE WHEN a=1 THEN CASE WHEN b=2 THEN 'X' -- 内层 CASE 缺少 END ELSE 'Y' END FROM table; ``` **报错原因**:内层 `CASE` 未闭合。 **修复方案**: ```sql SELECT CASE WHEN a=1 THEN CASE WHEN b=2 THEN 'X' ELSE 'Z' END -- 补全内层 END ELSE 'Y' END FROM table; ``` --- ### 四、调试建议 1. **逐段注释法**:逐步注释代码块定位错误位置 2. **语法高亮工具**:使用 pgAdmin、DBeaver 等工具直观检查语法结构 3. **简化复现**:提取最小代码片段测试 `CASE` 逻辑 --- 通过规范使用 `CASE` 表达式(注意闭合、条件完整性)和区分 PL/pgSQL 中的流程控制语句,可有效避免此类错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值