几种主流数据库特有的SQL查询语句语法模板

几种主流数据库特有的SQL查询语句语法模板前天我们一起把 269 条常用 SQL 查询语句 进行了归类 昨天我们又一起创建了 10 类 SQL 查询语句语法结构模板 今天我们应该开始拆解几种主流数据库特有查询语法

大家好,欢迎来到IT知识分享网。

前天我们一起把《269条常用SQL查询语句》进行了归类,昨天我们又一起创建了《10类SQL查询语句语法结构模板》,今天我们应该开始拆解几种主流数据库特有查询语法。先不看细枝末节,我们先看下面:(所有代码块或表格均可左右滚动)

一、MySQL特有:GROUP_CONCAT函数

GROUP_CONCAT是MySQL独有的聚合函数,将同一个分组内的多行数据按照指定规则合并为单个字符串,能够解决一对多关系中数据展示冗余的问题,将同一分组的多个关联值紧凑地聚合在一个字段中,广泛应用在报表生成、数据汇总、标签合并等场景。

GROUP_CONCAT函数支持去重、排序和自定义分隔符,灵活性高,我们可根据业务需求调整其合并规则。

1、查询语法模板及详细注释

SELECT -- 分组列:必须出现在GROUP BY中,用于定义“哪些行属于同一组” 分组列1, 分组列2, -- 核心函数:GROUP_CONCAT GROUP_CONCAT( [DISTINCT] 目标列, -- 可选参数:对目标列的值去重后再合并(去除重复数据) [ORDER BY 排序列 [ASC|DESC]], -- 可选参数:合并前按指定列排序(ASC升序,DESC降序,默认ASC) [SEPARATOR '自定义分隔符'] -- 可选参数:指定拼接分隔符(默认是逗号',') ) AS 合并结果别名 -- 为合并后的结果指定一个别名(便于读取) FROM 表名 -- 要查询的表 GROUP BY 分组列1, 分组列2; -- 按分组列分组(必须与SELECT中的分组列完全一致) 

2、创建模拟数据表及插入模拟数据

我们创建student_clubs表,用来存储学生参与的社团信息(一个学生可加入多个社团,存在一对多关系):

student_id(学生ID)

student_name(学生姓名)

club_name(社团名称)

join_time(加入时间)

1

张三

篮球社

2023-09-01

1

张三

编程社

2023-09-05

1

张三

篮球社

2023-09-01

2

李四

文学社

2023-09-02

3

王五

摄影社

2023-09-03

3

王五

编程社

2023-09-06

3

王五

摄影社

2023-09-03

注:有“篮球社”、“摄影社”重复项

3、应用场景示例及输出结果解读

示例1:基础合并(默认规则:保留重复值、逗号分隔、不排序)
场景:我们按学生分组,合并其加入的所有社团名称(包含重复项,使用默认逗号分隔)。

SELECT student_id AS 学生ID, student_name AS 学生姓名, GROUP_CONCAT(club_name) AS 参与社团 -- 不指定参数,使用默认规则 FROM student_clubs GROUP BY student_id, student_name; 

输出结果

学生ID

学生姓名

参与社团

1

张三

篮球社,编程社,篮球社

2

李四

文学社

3

王五

摄影社,编程社,摄影社

结果解读

  • 张三和王五因重复加入社团,结果中保留了重复的社团名称(如:“篮球社”出现2次);
  • 分隔符默认使用逗号“,”,未指定排序,因此按数据在表中的存储顺序合并(张三的社团顺序为“篮球社→编程社→篮球社”)。

示例2:去重合并(排除重复值)
场景:我们合并社团名称时去除重复项,仅保留唯一值。

SELECT student_id AS 学生ID, student_name AS 学生姓名, GROUP_CONCAT(DISTINCT club_name) AS 参与社团 -- 添加DISTINCT参数去重 FROM student_clubs GROUP BY student_id, student_name; 

输出结果

学生ID

学生姓名

参与社团

1

张三

篮球社,编程社

2

李四

文学社

3

王五

摄影社,编程社

结果解读

  • DISTINCT参数生效,重复的社团名称被去除(如:张三的“篮球社”仅保留1次);
  • 其他规则仍使用默认值(逗号分隔、不排序)。

示例3:排序合并(按指定列排序后合并)
场景:我们合并社团名称时,按加入时间(join_time)升序排序(先加入的社团排在前面)。

SELECT student_id AS 学生ID, student_name AS 学生姓名, GROUP_CONCAT( DISTINCT club_name -- 先去重 ORDER BY join_time ASC -- 按加入时间升序排序 ) AS 参与社团 FROM student_clubs GROUP BY student_id, student_name; 

输出结果

学生ID

学生姓名

参与社团

1

张三

篮球社,编程社

2

李四

文学社

3

王五

摄影社,编程社

注:第一行“篮球社(2023-09-01)早于编程社(2023-09-05)”;第三行“摄影社(2023-09-03)早于编程社(2023-09-06)”

结果解读

  • ORDER BY join_time ASC确保合并后的社团按加入时间从早到晚排序;GROUP_CONCAT的排序仅影响合并后的字符串内部顺序,不改变分组结果的整体顺序(分组顺序由GROUP BY决定);
  • 结合DISTINCT参数,既去重又保证了顺序性,结果更符合业务逻辑(按时间顺序展示学生参与的社团)。

示例4:自定义分隔符(使用特殊符号分隔)
场景:我们合并社团名称时,用“、”作为分隔符,并按照社团名称首字母降序排序。

SELECT student_id AS 学生ID, student_name AS 学生姓名, GROUP_CONCAT( DISTINCT club_name ORDER BY club_name DESC -- 按社团名称首字母降序排序(如:“编程社”→“篮球社”) SEPARATOR '、' -- 用“、”作为分隔符 ) AS 参与社团 FROM student_clubs GROUP BY student_id, student_name; 

输出结果

学生ID

学生姓名

参与社团

1

张三

编程社、篮球社

2

李四

文学社

3

王五

编程社、摄影社

注:第一行:“编程社”(B)首字母在“篮球社”(L)之后(降序);第三行:“编程社”(B)首字母在“摄影社”(S)之后(降序)

结果解读

  • SEPARATOR ‘、’将默认分隔符逗号改为“、”,使结果更易读,一目了然;
  • ORDER BY club_name DESC按社团名称首字母降序排序(中文按拼音排序),满足特定展示需求。

示例5:合并多列数据(拼接多个字段为复杂格式)
场景:我们合并“社团名称+加入时间”为“社团名(时间)”的格式,用分号“;”分隔。

SELECT student_id AS 学生ID, student_name AS 学生姓名, GROUP_CONCAT( DISTINCT CONCAT(club_name, '(', join_time, ')') -- 用CONCAT拼接多列 ORDER BY join_time ASC SEPARATOR ';' ) AS 社团及加入时间 FROM student_clubs GROUP BY student_id, student_name; 

输出结果

学生ID

学生姓名

社团及加入时间

1

张三

篮球社(2023-09-01);编程社(2023-09-05)

2

李四

文学社(2023-09-02)

3

王五

摄影社(2023-09-03);编程社(2023-09-06)

结果解读

  • 先用CONCAT(club_name, ‘(‘, join_time, ‘)’)将社团名称和加入时间拼接为“社团名(时间)”的格式(如:“篮球社(2023-09-01)”);
  • 再用GROUP_CONCAT合并这些格式字符串,实现多列数据的聚合展示,便于直观查看每个社团的加入时间。

小结:

GROUP_CONCAT函数能将分组内的多行数据“折叠”为单行字符串,通过DISTINCT(去重)、ORDER BY(排序)、SEPARATOR(自定义分隔符)等参数的组合,可实现多样化的聚合需求。

实际应用中,GROUP_CONCAT函数常用在:

  • 展示用户的多个标签、订单包含的多个商品等一对多关系数据;
  • 生成简洁的报表(如:将同一班级的学生姓名合并为一个字符串);
  • 拼接多列信息为结构化字符串(如:“社团+时间”、“商品+价格”)。

使用时,我们要注意:GROUP_CONCAT的结果长度受MySQL系统变量group_concat_max_len限制(默认1024字节),超长会被截断,我们可通过修改该变量调整其结果长度(如:SET GLOBAL group_concat_max_len = ;)。

二、PostgreSQL特有:JSONB函数

JSONB是PostgreSQL中二进制格式的JSON数据类型,相比普通JSON类型,支持索引且查询效率更高,非常适合存储半结构化数据(如:用户画像、商品属性、日志信息等)。

JSONB函数集提供了非常多的操作能力,包括:提取JSON中的键值、查询嵌套结构、修改JSON内容、拆分JSON数组等等,能够灵活处理动态变化的非结构化数据,无需预先定义严格的表结构。

1、常用JSONB函数语法模板及详细注释

函数/操作符

功能描述

语法模板(含注释)

->

根据键获取JSON值(返回JSONB类型)

jsonb_column -> ‘key’

— 从jsonb_column字段中,获取键为key的值(保留JSONB类型)

->>

根据键获取JSON值(返回文本类型)

jsonb_column ->> ‘key’

— 从jsonb_column字段中,获取键为key的值(转换为文本类型)

#>

根据路径获取嵌套JSON值(返回JSONB类型)

jsonb_column #> ‘{key1, key2}’

— 从嵌套结构中,获取key1下key2的值(保留JSONB类型)

#>>

根据路径获取嵌套JSON值(返回文本类型)

jsonb_column #>> ‘{key1, key2}’

— 从嵌套结构中,获取key1下key2的值(转换为文本类型)

@>

判断左侧JSON是否包含右侧JSON(返回布尔值)

jsonb_column @> ‘{“key”: “value”}’::jsonb

— 检查jsonb_column是否包含指定键值对

jsonb_set()

修改JSON中的指定值

jsonb_set(jsonb_column, ‘{key}’, ‘new_value’::jsonb)

— 将key对应的值修改为new_value(若键不存在则新增)

jsonb_array_elements

将JSON数组拆分为多行

jsonb_array_elements(jsonb_column -> ‘array_key’)

— 将array_key对应的JSON数组拆分为多行记录

注:补充jsonb_agg()函数,将多行数据聚合为 JSON 数组,在反向操作(行转JSON数组)中常用。

2、创建模拟数据表及插入模拟数据

我们创建product_details表,用来存储产品的动态属性(JSONB类型字段attributes):

product_id(产品ID)

attributes(产品属性,JSONB类型)

1

{“name”: “无线耳机”, “price”: 899, “specs”: {“brand”: “华为”, “color”: [“白色”, “黑色”], “battery”: “24小时”}, “tags”: [“降噪”, “热销”]}

2

{“name”: “智能手表”, “price”: 1599, “specs”: {“brand”: “苹果”, “color”: [“银色”], “battery”: “18小时”}, “tags”: [“运动”, “新品”]}

3

{“name”: “蓝牙音箱”, “price”: 399, “specs”: {“brand”: “小米”, “color”: [“灰色”, “蓝色”], “battery”: “12小时”}, “tags”: [“便携”, “热销”]}

3、应用场景示例及输出结果解读

示例1:提取基础键值与嵌套键值
场景:我们查询产品名称、价格、品牌和电池续航时间。

SELECT product_id AS 产品ID, attributes ->> 'name' AS 产品名称, -- 提取name(文本类型,可直接使用) (attributes -> 'price')::INT AS 价格, -- 提取price(JSONB类型,转换为整数) attributes #>> '{specs, brand}' AS 品牌, -- 提取specs下的brand(文本类型) attributes #>> '{specs, battery}' AS 电池续航 FROM product_details; 

输出结果

产品ID

产品名称

价格

品牌

电池续航

1

无线耳机

899

华为

24小时

2

智能手表

1599

苹果

18小时

3

蓝牙音箱

399

小米

12小时

结果解读

  • ->>直接返回文本类型(如:产品名称),无需转换即可作为字符串使用;
  • ->返回JSONB类型(如:价格),需通过::INT转换为整数才能进行数值计算;
  • #>>通过路径{specs, brand}获取嵌套在specs中的brand值,适用于多层级JSON结构。

示例2:查询包含指定标签的产品
场景:我们查询所有标签(tags)中包含“热销”的产品。

SELECT product_id AS 产品ID, attributes ->> 'name' AS 产品名称 FROM product_details -- 检查tags数组是否包含“热销”(@>表示左侧JSON包含右侧JSON) WHERE attributes -> 'tags' @> '["热销"]'::jsonb; 

输出结果

产品ID

产品名称

1

无线耳机

3

蓝牙音箱

结果解读

  • attributes -> ‘tags’获取tags数组(JSONB类型),@> ‘[“热销”]’::jsonb判断该数组是否包含“热销”元素;
  • 该语法适用于筛选包含特定元素的JSON数组(如:标签、权限列表等)。

示例3:将JSON数组拆分为多行
场景:我们将每个产品的颜色(color)数组拆分为多行,展示产品与颜色的对应关系。

SELECT product_id AS 产品ID, attributes ->> 'name' AS 产品名称, -- 将color数组拆分为多行,每行一个颜色(value为JSONB类型,需转为文本) jsonb_array_elements(attributes #> '{specs, color}') ->> 0 AS 颜色 -- jsonb_array_elements(attributes #> '{specs, color}')::text AS 颜色 FROM product_details; 

输出结果

产品ID

产品名称

颜色

1

无线耳机

白色

1

无线耳机

黑色

2

智能手表

银色

3

蓝牙音箱

灰色

3

蓝牙音箱

蓝色

结果解读

  • attributes #> ‘{specs, color}’获取specs下的color数组(如:[“白色”, “黑色”]);
  • jsonb_array_elements将数组拆分为多行,每个元素对应一行记录(如:无线耳机的颜色拆分为2行);
  • 拆分后可按颜色维度统计(如:“白色产品有多少种”),或与其他表关联查询。

示例4:修改JSON中的值(更新或新增键)
场景:我们将产品1的价格改为999,并新增“重量”属性(值为“30g”)。

-- 步骤1:修改价格 UPDATE product_details SET attributes = jsonb_set(attributes, '{price}', '999'::jsonb) -- 路径为{price},新值为999 WHERE product_id = 1; -- 步骤2:新增“重量”属性 UPDATE product_details SET attributes = jsonb_set(attributes, '{weight}', '"30g"'::jsonb) -- 新增weight键,值为"30g" WHERE product_id = 1; -- 查看修改结果 SELECT product_id, attributes FROM product_details WHERE product_id = 1; 

输出结果(attributes字段内容):

{ "name": "无线耳机", "price": 999, -- 价格已从899修改为999 "specs": {"brand": "华为", "color": ["白色", "黑色"], "battery": "24小时"}, "tags": ["降噪", "热销"], "weight": "30g" -- 新增的重量属性 } 

结果解读

  • jsonb_set函数支持修改已有键(如:price)和新增不存在的键(如weight);
  • 字符串类型的新值需用双引号包裹(如:”30g”),并通过::jsonb转换为JSONB类型;
  • 该功能无需修改表结构即可扩展数据字段,适合属性动态变化的场景(如:电商商品的个性化属性)。

示例5:查询JSON数组的长度
场景:我们查询每个产品的颜色选项数量(即color数组的长度)。

SELECT product_id AS 产品ID, attributes ->> 'name' AS 产品名称, -- 先获取color数组,再通过jsonb_array_length计算长度 jsonb_array_length(attributes #> '{specs, color}') AS 颜色选项数量 FROM product_details; 

输出结果

产品ID

产品名称

颜色选项数量

1

无线耳机

2

2

智能手表

1

3

蓝牙音箱

2

结果解读

  • jsonb_array_length:计算JSON数组的元素数量,此处通过该函数快速获取每个产品的颜色选项数;
  • 类似函数还有jsonb_typeof(判断JSON值的类型,如:“string”、“array”)等,可进一步扩展JSON处理能力。

小结:

PostgreSQL对JSON数据类型有原生支持,其中JSONB是二进制格式的JSON类型(支持索引、效率更高),提供了非常多的函数用于JSON数据的查询、修改和分析,适用于存储半结构化数据(如:用户画像、日志信息等)。JSONB函数处理半结构化数据的优势在于:

  1. 灵活性:无需预先定义表结构,可动态存储和扩展属性(如:产品的可变规格);
  2. 高效性:支持索引(如:GIN索引),查询性能优于普通JSON类型;
  3. 功能性:通过丰富的函数实现键值提取、数组拆分、内容修改等操作,满足多样化需求。

实际应用中,JSONB函数常用在存储用户配置、日志信息、电商商品属性等场景,尤其适合数据结构频繁变化或难以预先定义的业务场景。

三、SQL Server特有:PIVOT运算符

PIVOT是SQL Server专门用来实现“行转列”转换的运算符,能将表中某一列的不同行值(如:“月份”、“类别”)转换为新的列名,并通过聚合函数(如:SUM、AVG)计算对应的值,主要用于生成交叉报表或多维度数据对比表,将原本横向分布的数据纵向展示(如:将“每个地区的季度销售额”转换为“以季度为列、地区为行”的报表),使数据更直观易读。

1、查询语法模板及详细注释

-- 最终查询结果包含的列:非聚合列(保持为行)+ 转换后的新列(原行值) SELECT 非聚合列1, 非聚合列2, [新列1], [新列2], ..., [新列N] FROM ( -- 子查询:定义需要转换的基础数据(必须包含三类核心列) SELECT 分组列, -- 保持为行的列(如:“地区”、“部门”,不参与转换) 行值列, -- 需要转换为列名的列(如:“季度”、“月份”,其值将成为新列名) 聚合值列 -- 用于填充新列的数值(如:“销售额”、“数量”,需通过聚合函数计算) FROM 源表 -- 原始数据所在的表 -- 可选:添加WHERE子句过滤数据(如:只保留2023年的数据) WHERE 过滤条件 ) AS 子查询别名 -- 子查询必须指定别名 PIVOT ( -- 聚合函数:对聚合值列进行计算(常用SUM、AVG、COUNT等) 聚合函数(聚合值列) -- FOR子句:指定行值列,并定义其行值对应的新列名 FOR 行值列 IN ( [新列1], -- 对应行值列的某个值(如:“Q1”、“1月”) [新列2], -- 对应行值列的另一个值(如:“Q2”、“2月”) ... [新列N] -- 所有需要转换的行值都必须手动列举 ) ) AS pivot_table别名; -- 转换后的结果集必须指定别名 

语法元素解析

(1)子查询(基础数据集)

  • 作用:定义PIVOT操作的原始数据,需包含三类关键列:
    • 分组列:转换后仍作为行标识的列(不参与行转列,如:“姓名”、“日期”)。
    • 行转列的基准列:其值将被转换为新的列名(如:“课程名称”、“月份”)。
    • 需要聚合的列:将被聚合函数计算后填充到新列中的数据(如:“分数”、“销售额”)。
  • 示例:若原始表是student_scores,子查询可提取学生姓名(分组列)、课程(基准列)、分数(聚合列)。

(2)PIVOT关键字后的聚合函数

  • 作用:对“需要聚合的列”进行计算,生成新列的值。
  • 支持的函数:SUM、COUNT、AVG、MAX、MIN等,需根据业务场景选择。
  • 示例:SUM(分数)表示对同一学生、同一课程的分数求和(若存在重复数据)。

(3)FOR [行转列的基准列]

  • 作用:指定以哪一列的“值”作为转换后新列的名称。
  • 限制:基准列的类型通常为字符串或数字(需能作为列名),且其值需在IN子句中明确枚举。

(4)IN ([值1], [值2], …)

  • 作用:明确列出基准列中需要转换为新列的具体值,未列出的值将被忽略。
  • 注意事项:
    • 若值中包含特殊字符(如:空格、数字开头),需用方括号[]包裹(如:[语文]、[1月])。
    • 可通过AS为新列指定别名(如:[语文] AS 语文成绩)。
  • 示例:IN ([语文], [数学], [英语])表示将“课程”列中的“语文”、“数学”、“英语”转换为新列。

(5)最终SELECT子句

  • 作用:从PIVOT转换后的结果集中选择需要的列。
  • 可包含:
    • 子查询中的“分组列”(作为行标识)。
    • IN子句中定义的新列(由PIVOT生成)。

注意事项
(1)IN子句必须明确枚举所有需要转换的基准列值,无法动态生成(若值不固定,需用动态SQL)。
(2)子查询中不能包含多余的列,否则可能导致聚合错误(仅保留分组列、基准列、聚合列)。
(3)若基准列的值重复(如:同一地区同一月份有多个销售额记录),聚合函数(如:SUM)会自动合并计算。


我们通过PIVOT,可以快速将行数据转换为更直观的列结构,适合报表生成、数据汇总等业务场景。

2、创建模拟数据表及插入模拟数据

我们创建region_sales表,用来存储各地区的季度销售额数据:

region(地区)

quarter(季度)

amount(销售额)

year(年份)

华北

Q1

50000

2023

华北

Q2

60000

2023

华北

Q3

75000

2023

华东

Q1

80000

2023

华东

Q2

90000

2023

华东

Q3

2023

华东

Q4

2023

华南

Q1

40000

2023

华南

Q2

55000

2023

3、应用场景示例及输出结果解读

示例1:基础行转列(固定列名,单分组列)
场景:我们将“季度”行转换为列(Q1、Q2、Q3、Q4),展示各地区2023年的季度销售额。

SELECT 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/189136.html

(0)
上一篇 2025-09-28 10:15
下一篇 2025-09-28 10:20

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信