教程对应分组与聚合函数、运算符两大核心章节,基于医院门诊挂号业务表设计,包含完整语法定义、规则说明、业务场景示例 。
前置准备:业务表与测试数据
1. 操作环境说明
执行用户:普通业务用户
user_hospital数据库:
db_cloud_hospital(云院业务库)gsql提示符:
db_cloud_hospital=>核心业务表:
t_outpatient_reg(门诊挂号表,覆盖全量业务场景)
2. 业务表结构定义
-- 门诊挂号表(全量字段适配后续所有示例)
DROP TABLE IF EXISTS t_outpatient_reg;
CREATE TABLE t_outpatient_reg (
reg_id INT PRIMARY KEY, -- 挂号ID(主键,唯一标识)
patient_name VARCHAR(50) NOT NULL, -- 用户姓名
patient_gender CHAR(1), -- 用户性别(男/女)
patient_age INT, -- 用户年龄
patient_phone VARCHAR(20), -- 用户手机号
department VARCHAR(50) NOT NULL, -- 就诊科室
doctor_name VARCHAR(50), -- 接诊医
reg_fee NUMERIC(5,2) NOT NULL, -- 挂号婓
reg_time DATE DEFAULT CURRENT_DATE -- 挂号日期(年月日)
);
3. 测试数据(全量覆盖业务场景)
-- 插入12条挂号测试数据
INSERT INTO t_outpatient_reg
VALUES
(1001, '张三', '男', 35, '13800138000', '内科', '王医', 25.00, '2026-03-24'),
(1002, '李四', '女', 28, '13900139000', '外科', '李医', 30.00, '2026-03-24'),
(1003, '王五', '男', 45, '13700137000', '儿科', '刘医', 20.00, '2026-03-24'),
(1004, '赵六', '女', 32, '13600136000', '内科', '王医', 25.00, '2026-03-25'),
(1005, '阿七', '男', 52, '13500135000', '外科', '张医', 30.00, '2026-03-25'),
(1006, '孙八', '女', 6, '13400134000', '儿科', '刘医', 20.00, '2026-03-25'),
(1007, '周九', '男', 29, '13300133000', '内科', '郑医', 35.00, '2026-03-25'),
(1008, '吴十', '女', 41, '13200132000', '妇产科', '陈医', 40.00, '2026-03-24'),
(1009, '郑十一', '男', 38, '13100131000', '骨科', '杨医', 30.00, '2026-03-24'),
(1010, '王十二', '女', 24, '13000130000', '皮肤科', '黄医', 22.00, '2026-03-25'),
(1011, '张十三', '男', 58, NULL, '内科', '王医', 25.00, '2026-03-24'),
(1012, '李十四', '女', 33, '13800138001', '外科', NULL, 30.00, '2026-03-25');
一、分组查询与聚合函数
本章节核心解决数据分类统计需求,是医院业务中「科室挂号量统计、收入核算、均值分析」的核心技术手段。
1.1 分组查询(GROUP BY)
1. 核心作用
根据指定字段对查询结果进行分组,字段值相同的记录会被合并为一条数据,是分类统计的基础。
2. 完整语法定义
SELECT [查询选项] 字段名[, ...] | 聚合函数
FROM 数据表名称
[WHERE 条件表达式]
GROUP BY 分组字段名[, ...];
3. 核心规则说明
SELECT子句中,非聚合函数的普通字段,必须全部出现在 GROUP BY 子句中(openGauss严格语法规范);可按单个字段分组,也可按多个字段组合分组;
WHERE子句在分组前执行,用于过滤原始数据。
4. 医院业务场景示例
示例1:单字段分组(按科室分组,查询所有挂号科室)
-- 按科室分组,获取所有有挂号记录的科室列表
SELECT department AS 就诊科室
FROM t_outpatient_reg
GROUP BY department;
示例2:多字段组合分组(按科室+挂号日期分组)
-- 统计每个科室、每天的挂号维度数据
SELECT department AS 就诊科室, reg_time AS 挂号日期
FROM t_outpatient_reg
GROUP BY department, reg_time;
1.2 聚合函数
聚合函数用于对一组值进行运算,最终返回单个统计结果,90%的统计场景都需要聚合函数+分组配合使用。
1. 常用聚合函数总览
| 聚合函数 | 功能描述 | 医院业务典型场景 |
|---|---|---|
COUNT() |
返回参数字段的行数,参数为*时统计总记录数 |
统计科室挂号量、总就诊人数 |
SUM() |
返回参数字段的数值总和,忽略NULL值 | 统计科室挂号婓总收入、单日营收 |
AVG() |
返回参数字段的数值平均值,忽略NULL值 | 统计平均挂号婓、用户平均年龄 |
MAX() |
返回参数字段的最大值 | 查询最高挂号婓、用户最大年龄 |
MIN() |
返回参数字段的最小值 | 查询最低挂号婓、用户最小年龄 |
2. 各函数语法与业务示例
(1)COUNT() 计数函数
语法格式:
SELECT COUNT(* | 字段名) FROM 数据表名称 [WHERE 条件];
医院场景示例:
-- 1. 统计全院总挂号记录数(包含NULL值行)
SELECT COUNT(*) AS 全院总挂号量 FROM t_outpatient_reg;
-- 2. 统计有手机号的用户挂号数(忽略phone为NULL的行)
SELECT COUNT(patient_phone) AS 有效手机号挂号量 FROM t_outpatient_reg;
-- 3. 统计每个科室的挂号量(分组+聚合配合使用,核心场景)
SELECT department AS 就诊科室, COUNT(reg_id) AS 挂号量
FROM t_outpatient_reg
GROUP BY department;
(2)SUM() 求和函数
语法格式:
SELECT SUM(数值字段名) FROM 数据表名称 [WHERE 条件];
医院场景示例:
-- 1. 统计全院挂号婓总收入
SELECT SUM(reg_fee) AS 全院挂号婓总收入_元 FROM t_outpatient_reg;
-- 2. 统计内科挂号婓总收入
SELECT SUM(reg_fee) AS 内科挂号婓总收入_元
FROM t_outpatient_reg
WHERE department = '内科';
-- 3. 统计每个科室的挂号婓总收入(分组+聚合)
SELECT department AS 就诊科室, SUM(reg_fee) AS 科室总收入_元
FROM t_outpatient_reg
GROUP BY department;
(3)AVG() 平均值函数
语法格式:
SELECT AVG(数值字段名) FROM 数据表名称 [WHERE 条件];
医院场景示例:
-- 1. 统计全院挂号用户的平均年龄
SELECT AVG(patient_age) AS 用户平均年龄 FROM t_outpatient_reg;
-- 2. 统计全院平均挂号婓
SELECT AVG(reg_fee) AS 平均挂号婓_元 FROM t_outpatient_reg;
-- 3. 统计每个科室的平均挂号婓(分组+聚合)
SELECT department AS 就诊科室, AVG(reg_fee) AS 科室平均挂号婓_元
FROM t_outpatient_reg
GROUP BY department;
(4)MAX()/MIN() 极值函数
语法格式:
SELECT MAX(字段名), MIN(字段名) FROM 数据表名称 [WHERE 条件];
医院场景示例:
-- 1. 查询全院最高/最低挂号婓
SELECT MAX(reg_fee) AS 最高挂号婓_元, MIN(reg_fee) AS 最低挂号婓_元
FROM t_outpatient_reg;
-- 2. 查询就诊用户的最大/最小年龄
SELECT MAX(patient_age) AS 最大就诊年龄, MIN(patient_age) AS 最小就诊年龄
FROM t_outpatient_reg;
-- 3. 统计每个科室的最高挂号婓(分组+聚合)
SELECT department AS 就诊科室, MAX(reg_fee) AS 科室最高挂号婓_元
FROM t_outpatient_reg
GROUP BY department;
1.3 分组后筛选(GROUP BY + HAVING)
1. 核心作用
对分组聚合后的结果进行条件筛选,仅返回符合条件的分组。
2. 完整语法定义
SELECT [查询选项] 字段名[, ...] | 聚合函数
FROM 数据表名称
[WHERE 条件表达式]
GROUP BY 分组字段名[, ...]
HAVING 聚合函数条件表达式;
3. 核心规则:HAVING 与 WHERE 的区别
| 特性 | WHERE 子句 | HAVING 子句 |
|---|---|---|
| 执行时机 | 分组前执行,过滤原始行 | 分组后执行,过滤分组结果 |
| 支持内容 | 仅能使用表中原始字段,不能使用聚合函数 | 仅能使用GROUP BY字段、聚合函数 |
| 性能 | 先过滤再分组,数据量小,性能更高 | 先全量分组再过滤,大数据量性能较低 |
4. 医院业务场景示例
示例1:筛选挂号量超过2条的科室
SELECT department AS 就诊科室, COUNT(reg_id) AS 挂号量
FROM t_outpatient_reg
GROUP BY department
HAVING COUNT(reg_id) > 2;
示例2:筛选挂号婓总收入超过50元的科室
SELECT department AS 就诊科室, SUM(reg_fee) AS 科室总收入_元
FROM t_outpatient_reg
GROUP BY department
HAVING SUM(reg_fee) > 50;
示例3:WHERE + GROUP BY + HAVING 组合使用
-- 先过滤2026-03-24的挂号记录,再按科室分组,最后筛选挂号量≥2的科室
SELECT department AS 就诊科室, COUNT(reg_id) AS 当日挂号量
FROM t_outpatient_reg
WHERE reg_time = '2026-03-24'
GROUP BY department
HAVING COUNT(reg_id) >= 2;
1.4 分组+排序+限量 综合实战
医院业务高频场景:统计各科室挂号核心指标,按总收入降序取前3名
SELECT
department AS 就诊科室,
COUNT(reg_id) AS 总挂号量,
SUM(reg_fee) AS 总收入_元,
AVG(reg_fee) AS 平均挂号婓_元,
MAX(reg_fee) AS 最高挂号婓_元
FROM t_outpatient_reg
GROUP BY department
ORDER BY 总收入_元 DESC
LIMIT 3;
二、运算符详解
运算符是openGauss中构建条件表达式、完成数据运算的核心基础,本章节覆盖算术、比较、逻辑、赋值、位运算符全类型,所有示例均基于门诊挂号表。
2.1 算术运算符
算术运算符用于数值型数据的数学运算,仅支持数值类型字段(如挂号婓、年龄)。
1. 常用算术运算符与规则
| 运算符 | 功能描述 | 核心规则与注意事项 |
|---|---|---|
+ |
加法运算 | 浮点数加减运算,结果精度=参与运算数的最大精度 |
- |
减法运算 | 同上 |
* |
乘法运算 | 浮点数乘法运算,结果精度=参与运算数的精度和 |
/ |
除法运算 | 保留小数结果,除数为0时报错 |
DIV |
整数除法 | 舍弃小数部分,仅返回整数结果 |
% / MOD
|
取模/取余运算 | 返回除法运算的余数 |
NULL参与运算 |
- | NULL参与任何算术运算,结果均为NULL |
2. 医院业务场景示例
示例1:基础算术运算(挂号婓调价)
-- 1. 挂号婓上调5元,查询调价前后对比
SELECT
reg_id, patient_name, department,
reg_fee AS 原挂号婓,
reg_fee + 5 AS 上调后挂号婓,
reg_fee * 1.2 AS 涨价20%后挂号婓,
reg_fee - 2 AS 便宜2元后挂号婓
FROM t_outpatient_reg;
-- 2. 整数除法与普通除法对比
SELECT
reg_fee AS 挂号婓,
reg_fee / 5 AS 普通除法结果,
reg_fee DIV 5 AS 整数除法结果
FROM t_outpatient_reg;
-- 3. 取模运算(判断挂号婓是否为偶数)
SELECT reg_fee, reg_fee MOD 2 AS 取模结果
FROM t_outpatient_reg;
示例2:NULL值参与算术运算
-- 手机号为NULL的行,运算结果均为NULL
SELECT patient_name, patient_phone, patient_age + 5 AS 年龄加5
FROM t_outpatient_reg
WHERE patient_phone IS NULL;
示例3:精度运算规则
-- 加法:最大精度3,结果精度3
SELECT 1.2 + 1.400 AS 加法精度示例;
-- 乘法:精度1+3=4,结果精度4
SELECT 1.2 * 1.400 AS 乘法精度示例;
2.2 比较运算符
比较运算符用于条件表达式中的值判断,运算结果仅为3种:1(TRUE真)、0(FALSE假)、NULL(未知),是WHERE子句的核心组成。
1. 常用比较运算符分类与说明
(1)基础比较运算符
| 运算符 | 功能描述 | 核心注意事项 |
|---|---|---|
= |
相等判断 | 与NULL比较结果为NULL |
<=> |
安全相等判断 | 支持NULL值比较,两个NULL比较返回1(TRUE) |
<> / !=
|
不相等判断 | 与NULL比较结果为NULL |
< |
小于判断 | - |
<= |
小于等于判断 | - |
> |
大于判断 | - |
>= |
大于等于判断 | - |
(2)范围与空值判断运算符
| 运算符 | 功能描述 |
|---|---|
BETWEEN ... AND ... |
判断值是否在指定闭区间内(包含首尾值) |
NOT BETWEEN ... AND ... |
判断值是否不在指定闭区间内 |
IS NULL |
判断值是否为NULL |
IS NOT NULL |
判断值是否不为NULL |
IS |
判断值是否为TRUE/FALSE/NULL |
IS NOT |
判断值是否不是TRUE/FALSE/NULL |
(3)模糊匹配与正则运算符
| 运算符 | 功能描述 | 核心通配符/规则 |
|---|---|---|
LIKE |
模糊匹配查询 |
%匹配任意长度字符串,_匹配单个字符 |
NOT LIKE |
反向模糊匹配 | 同上 |
REGEXP |
正则表达式匹配 | 支持正则语法,` |
2. 核心使用注意事项
字符串、日期类型的值,必须用单引号包裹;
字符串比较默认不区分大小写;
数字与字符串比较时,会自动转换为同类型后再比较;
=、<>等基础运算符与NULL比较,结果均为NULL,空值判断必须用IS NULL/IS NOT NULL。
3. 医院业务场景示例
示例1:基础比较运算符
-- 1. 查询挂号婓等于30元的挂号记录
SELECT * FROM t_outpatient_reg WHERE reg_fee = 30.00;
-- 2. 查询年龄大于40岁的用户挂号记录
SELECT * FROM t_outpatient_reg WHERE patient_age > 40;
-- 3. 查询非内科的挂号记录
SELECT * FROM t_outpatient_reg WHERE department != '内科';
-- 4. 安全相等判断<=>(对比=的区别)
-- 结果:仅返回doctor_name为NULL的行
SELECT * FROM t_outpatient_reg WHERE doctor_name <=> NULL;
-- 结果:无返回(=与NULL比较结果为NULL)
SELECT * FROM t_outpatient_reg WHERE doctor_name = NULL;
示例2:范围判断运算符
-- 1. 查询挂号婓在20-30元之间的记录(包含20和30)
SELECT * FROM t_outpatient_reg
WHERE reg_fee BETWEEN 20.00 AND 30.00;
-- 2. 查询年龄在25-40岁之外的用户记录
SELECT * FROM t_outpatient_reg
WHERE patient_age NOT BETWEEN 25 AND 40;
-- 3. 查询挂号日期在2026-03-24至2026-03-25之间的记录
SELECT * FROM t_outpatient_reg
WHERE reg_time BETWEEN '2026-03-24' AND '2026-03-25';
示例3:空值判断运算符
-- 1. 查询未分配接诊医的挂号记录
SELECT * FROM t_outpatient_reg WHERE doctor_name IS NULL;
-- 2. 查询手机号已填写的用户记录
SELECT * FROM t_outpatient_reg WHERE patient_phone IS NOT NULL;
示例4:模糊匹配运算符(LIKE)
-- 1. 查询所有姓张的用户记录(%匹配任意长度字符)
SELECT * FROM t_outpatient_reg WHERE patient_name LIKE '张%';
-- 2. 查询姓名以"医"结尾的接诊医记录
SELECT * FROM t_outpatient_reg WHERE doctor_name LIKE '%医';
-- 3. 查询姓名为3个字的用户记录(_匹配单个字符,2个_匹配2个字符)
SELECT * FROM t_outpatient_reg WHERE patient_name LIKE '___';
-- 4. 查询手机号以138开头的用户记录
SELECT * FROM t_outpatient_reg WHERE patient_phone LIKE '138%';
示例5:正则匹配运算符(REGEXP)
-- 1. 查询姓名中包含"张"或"李"的用户记录(|表示或)
SELECT * FROM t_outpatient_reg WHERE patient_name REGEXP '张|李';
-- 2. 查询手机号以13开头、以0结尾的用户记录(^开头,$结尾)
SELECT * FROM t_outpatient_reg WHERE patient_phone REGEXP '^13.*0$';
2.3 逻辑运算符
逻辑运算符用于多个条件表达式的组合判断,通常与比较运算符配合使用,运算结果为1(TRUE)、0(FALSE)、NULL(未知)。
1. 常用逻辑运算符
| 运算符 | 功能描述 | 核心运算规则 | ||
|---|---|---|---|---|
NOT / !
|
逻辑非 | 返回与操作数相反的结果,真→假,假→真,NULL→NULL | ||
AND / &&
|
逻辑与 | 所有操作数都为真,结果才为真;有一个为假,结果为假 | ||
OR / ` |
` | |||
XOR |
逻辑异或 | 两个操作数一真一假,结果为真;全真/全假,结果为假 |
2. NULL值参与逻辑运算的规则
AND:含NULL时,另一个操作数为真→结果NULL,另一个为假→结果0;OR:含NULL时,另一个操作数为真→结果1,另一个为假→结果NULL;NOT:NOT NULL结果为NULL。
3. 医院业务场景示例
示例1:AND 逻辑与(多条件同时满足)
-- 查询2026-03-24、内科、挂号婓25元的挂号记录
SELECT * FROM t_outpatient_reg
WHERE reg_time = '2026-03-24'
AND department = '内科'
AND reg_fee = 25.00;
示例2:OR 逻辑或(满足任一条件即可)
-- 查询内科或外科的挂号记录
SELECT * FROM t_outpatient_reg
WHERE department = '内科' OR department = '外科';
示例3:NOT 逻辑非(反向条件)
-- 查询非儿科、非妇产科的挂号记录
SELECT * FROM t_outpatient_reg
WHERE NOT department IN ('儿科', '妇产科');
示例4:XOR 逻辑异或(二选一)
-- 查询"挂号婓大于30元"和"科室是内科"两个条件仅满足一个的记录
SELECT * FROM t_outpatient_reg
WHERE reg_fee > 30.00 XOR department = '内科';
示例5:多运算符组合使用(括号优先执行)
-- 查询(内科或外科)且(年龄大于30岁)的用户记录
SELECT * FROM t_outpatient_reg
WHERE (department = '内科' OR department = '外科')
AND patient_age > 30;
2.4 赋值运算符
赋值运算符用于字段更新、变量赋值,openGauss支持两种赋值运算符。
| 运算符 | 功能描述 | 核心使用场景 | 注意事项 |
|---|---|---|---|
= |
双重功能:比较判断 + 赋值 | UPDATE语句中给字段赋值 | WHERE子句中=为比较运算符,SET子句中=为赋值运算符 |
:= |
仅赋值功能 | 变量赋值,避免与比较运算符歧义 | 仅用于赋值场景,不会被识别为比较运算符 |
业务场景示例
示例1:UPDATE中的赋值运算符
-- 将儿科所有挂号婓上调3元(SET子句中=为赋值运算符)
UPDATE t_outpatient_reg
SET reg_fee = reg_fee + 3
WHERE department = '儿科';
-- WHERE子句中=为比较运算符,SET子句中=为赋值运算符
UPDATE t_outpatient_reg
SET doctor_name = '王医'
WHERE reg_id = 1012;
示例2:变量赋值(:=避免歧义)
-- 声明变量并赋值,:=仅作为赋值,不会被识别为比较
\set total_reg_fee := (SELECT SUM(reg_fee) FROM t_outpatient_reg);
-- 查看变量值
SELECT :total_reg_fee AS 全院总挂号婓;
2.5 位运算符
位运算符针对二进制数的每一位进行运算,openGauss会自动将数值转换为64位无符号整数后逐位运算。
| 运算符 | 功能描述 | 核心运算规则 | |
|---|---|---|---|
& |
按位与 | 对应二进制位全为1,结果为1,否则为0 | |
| ` | ` | 按位或 | |
^ |
按位异或 | 对应二进制位不同,结果为1,否则为0 | |
<< |
按位左移 | 二进制位左移指定位数,高位丢弃,低位补0 | |
>> |
按位右移 | 二进制位右移指定位数,低位丢弃,高位补0 | |
~ |
按位取反 | 二进制位0变1,1变0 |
示例
-- 按位与:3(011) & 5(101) = 1(001)
SELECT 3 & 5;
-- 按位或:3(011) | 5(101) = 7(111)
SELECT 3 | 5;
-- 按位左移:3(011) << 1 = 6(110)
SELECT 3 << 1;
-- 按位右移:5(101) >> 1 = 2(10)
SELECT 5 >> 1;
2.6 运算符优先级
运算符优先级决定了表达式中运算的先后顺序,优先级越高,越早执行,完整优先级从高到低如下表:
| 优先级(从高到低) | 运算符 |
|---|---|
| 1 | 括号()(最内层括号优先级最高) |
| 2 |
!、一元负号-、按位取反~
|
| 3 | ^ |
| 4 |
*、/、DIV、%、MOD
|
| 5 |
+、-
|
| 6 |
<<、>>
|
| 7 | & |
| 8 | ` |
| 9 |
=、<=>、>=、>、<=、<、<>、!=、IS、LIKE、REGEXP
|
| 10 | NOT |
| 11 | AND |
| 12 | XOR |
| 13 | OR |
| 14 |
=(赋值)、:=
|
核心注意事项
不确定优先级时,使用括号
()包裹需要优先执行的表达式,是最稳妥、可读性最高的方式;多条件组合时,括号内的表达式会优先执行,避免因优先级导致的逻辑错误。
三、综合实战案例(全知识点融合)
医院业务需求:统计2026-03-24日,各科室的挂号运营指标,筛选出挂号量≥2、平均挂号婓≥25元的科室,按总收入降序排列。
SELECT
department AS 就诊科室,
COUNT(reg_id) AS 当日挂号量,
SUM(reg_fee) AS 当日总收入_元,
AVG(reg_fee) AS 当日平均挂号婓_元,
MAX(patient_age) AS 最大就诊年龄,
MIN(patient_age) AS 最小就诊年龄
FROM t_outpatient_reg
WHERE reg_time = '2026-03-24' -- 分组前过滤日期
GROUP BY department
HAVING COUNT(reg_id) >= 2 AND AVG(reg_fee) >= 25 -- 分组后筛选
ORDER BY 当日总收入_元 DESC; -- 排序
(注:)