几种主流数据库特有的SQL查询语句语法模板
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
前天我们一起把《269条常用SQL查询语句》进行了归类,昨天我们又一起创建了《10类SQL查询语句语法结构模板》,今天我们应该开始拆解几种主流数据库特有查询语法。先不看细枝末节,我们先看下面:(所有代码块或表格均可左右滚动) 一、MySQL特有:GROUP_CONCAT函数
1、查询语法模板及详细注释
2、创建模拟数据表及插入模拟数据我们创建 注:有“篮球社”、“摄影社”重复项 3、应用场景示例及输出结果解读示例1:基础合并(默认规则:保留重复值、逗号分隔、不排序)
输出结果: 结果解读:
示例2:去重合并(排除重复值)
输出结果: 结果解读:
示例3:排序合并(按指定列排序后合并)
输出结果: 注:第一行“篮球社(2023-09-01)早于编程社(2023-09-05)”;第三行“摄影社(2023-09-03)早于编程社(2023-09-06)” 结果解读:
示例4:自定义分隔符(使用特殊符号分隔)
输出结果: 注:第一行:“编程社”(B)首字母在“篮球社”(L)之后(降序);第三行:“编程社”(B)首字母在“摄影社”(S)之后(降序) 结果解读:
示例5:合并多列数据(拼接多个字段为复杂格式)
输出结果: 结果解读:
小结:
实际应用中,
使用时,我们要注意: 二、PostgreSQL特有:JSONB函数
1、常用 |
-> | JSONB 类型) | jsonb_column -> 'key' jsonb_column 字段中,获取键为key 的值(保留JSONB类型) |
->> | jsonb_column ->> 'key' jsonb_column 字段中,获取键为key 的值(转换为文本类型) | |
#> | JSONB 类型) | jsonb_column #> '{key1, key2}' key1 下key2 的值(保留JSONB类型) |
#>> | jsonb_column #>> '{key1, key2}' key1 下key2 的值(转换为文本类型) | |
@> | jsonb_column @> '{"key": "value"}'::jsonb jsonb_column 是否包含指定键值对 | |
jsonb_set() | jsonb_set(jsonb_column, '{key}', 'new_value'::jsonb) key 对应的值修改为new_value (若键不存在则新增) | |
jsonb_array_elements | jsonb_array_elements(jsonb_column -> 'array_key') array_key 对应的JSON数组拆分为多行记录 |
注:补充jsonb_agg()
函数,将多行数据聚合为 JSON 数组,在反向操作(行转JSON
数组)中常用。
我们创建product_details
表,用来存储产品的动态属性(JSONB
类型字段attributes
):
{"name": "无线耳机", "price": 899, "specs": {"brand": "华为", "color": ["白色", "黑色"], "battery": "24小时"}, "tags": ["降噪", "热销"]}
{"name": "智能手表", "price": 1599, "specs": {"brand": "苹果", "color": ["银色"], "battery": "18小时"}, "tags": ["运动", "新品"]}
{"name": "蓝牙音箱", "price": 399, "specs": {"brand": "小米", "color": ["灰色", "蓝色"], "battery": "12小时"}, "tags": ["便携", "热销"]}
示例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;
输出结果:
结果解读:
->>
直接返回文本类型(如:产品名称),无需转换即可作为字符串使用;->
返回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;
输出结果:
结果解读:
attributes -> 'tags'
获取tags
数组(JSONB
类型),@> '["热销"]'::jsonb
判断该数组是否包含“热销”元素;示例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;
输出结果:
结果解读:
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;
输出结果:
结果解读:
jsonb_array_length
:计算JSON数组的元素数量,此处通过该函数快速获取每个产品的颜色选项数;jsonb_typeof
(判断JSON值的类型,如:“string”、“array”)等,可进一步扩展JSON处理能力。PostgreSQL对JSON数据类型有原生支持,其中JSONB
是二进制格式的JSON类型(支持索引、效率更高),提供了非常多的函数用于JSON数据的查询、修改和分析,适用于存储半结构化数据(如:用户画像、日志信息等)。JSONB
函数处理半结构化数据的优势在于:
GIN
索引),查询性能优于普通JSON
类型;实际应用中,JSONB
函数常用在存储用户配置、日志信息、电商商品属性等场景,尤其适合数据结构频繁变化或难以预先定义的业务场景。
PIVOT
是SQL Server专门用来实现“行转列”转换的运算符,能将表中某一列的不同行值(如:“月份”、“类别”)转换为新的列名,并通过聚合函数(如:SUM
、AVG
)计算对应的值,主要用于生成交叉报表或多维度数据对比表,将原本横向分布的数据纵向展示(如:将“每个地区的季度销售额”转换为“以季度为列、地区为行”的报表),使数据更直观易读。
-- 最终查询结果包含的列:非聚合列(保持为行)+ 转换后的新列(原行值)
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
,可以快速将行数据转换为更直观的列结构,适合报表生成、数据汇总等业务场景。
我们创建region_sales
表,用来存储各地区的季度销售额数据:
示例1:基础行转列(固定列名,单分组列)
场景:我们将“季度”行转换为列(Q1、Q2、Q3、Q4),展示各地区2023年的季度销售额。
SELECT
region AS 地区,
[Q1] AS 一季度销售额, -- 对应原quarter列的“Q1”
[Q2] AS 二季度销售额,
[Q3] AS 三季度销售额,
[Q4] AS 四季度销售额
FROM (
-- 子查询:提取地区、季度、销售额(过滤2023年数据)
SELECT region, quarter, amount
FROM region_sales
WHERE year = 2023
) AS source_data
PIVOT (
SUM(amount) -- 对销售额求和(因每个地区+季度唯一,SUM等效于直接取值)
FOR quarter IN ([Q1], [Q2], [Q3], [Q4]) -- 将季度行值转换为列名
) AS pivot_sales;
输出结果:
注:华北无Q4数据,显示NULL;华南无Q3、Q4数据。实际业务中可通过ISNULL函数替换为默认值(如:0,示例:ISNULL([Q4], 0) AS 四季度销售额
)
结果解读:
region
是分组列(保持为行),quarter
是行值列(转换为列名),amount
是聚合值列(填充新列);PIVOT
将quarter
列的“Q1”、“Q2”等行值转换为新列名,并用SUM(amount)
计算对应的值;NULL
(可通过ISNULL
函数替换为0,如:ISNULL([Q4], 0) AS 四季度销售额
)。示例2:多分组列的行转列(按多个维度分组)
场景:按“年份+地区”分组,我们将“季度”转换为列,展示各年份各地区的季度销售额。
SELECT
year AS 年份,
region AS 地区,
[Q1] AS 一季度销售额,
[Q2] AS 二季度销售额,
[Q3] AS 三季度销售额
FROM (
-- 子查询:包含年份、地区、季度、销售额(多了一个分组列year)
SELECT year, region, quarter, amount
FROM region_sales
WHERE quarter IN ('Q1', 'Q2', 'Q3') -- 只保留Q1-Q3
) AS source_data
PIVOT (
SUM(amount)
FOR quarter IN ([Q1], [Q2], [Q3])
) AS pivot_sales
ORDER BY year, region;
输出结果:
结果解读:
year
作为分组列,最终结果按“年份+地区”两级维度展示;示例3:使用不同聚合函数(非SUM场景)
场景:统计各地区每个季度的销售记录数(而非销售额),我们将季度转换为列。
SELECT
region AS 地区,
[Q1] AS Q1记录数,
[Q2] AS Q2记录数,
[Q3] AS Q3记录数,
[Q4] AS Q4记录数
FROM (
SELECT region, quarter, amount -- amount仅用于存在性判断,不参与计算
FROM region_sales
) AS source_data
PIVOT (
COUNT(amount) -- 用COUNT统计记录数(只要amount非NULL就计数)
FOR quarter IN ([Q1], [Q2], [Q3], [Q4])
) AS pivot_count;
输出结果:
注:华北Q4无记录,COUNT返回0,等等
结果解读:
PIVOT
支持多种聚合函数,此处COUNT(amount)
统计每个地区每个季度的记录数量;SUM
不同,COUNT
对空值(无记录)返回0,更适合统计“次数”、“数量”类指标。示例4:动态生成列(适应行值不固定的场景)
场景:自动识别表中所有存在的季度(无需手动列举),动态转换为列(适用于季度值可能新增的场景)。
DECLARE
@columns NVARCHAR(MAX), -- 存储动态列名(如:[Q1],[Q2],[Q3],[Q4])
@sql NVARCHAR(MAX); -- 存储动态生成的SQL语句
-- 步骤1:动态获取所有季度作为列名(用QUOTENAME处理特殊字符,STRING_AGG拼接)
SELECT @columns = STRING_AGG(QUOTENAME(quarter), ',')
FROM (SELECT DISTINCT quarter FROM region_sales) AS quarters;
-- 步骤2:拼接动态SQL(将@columns代入PIVOT语句)
SET @sql = N'
SELECT region AS 地区, ' + @columns + N'
FROM (
SELECT region, quarter, amount FROM region_sales
) AS source_data
PIVOT (
SUM(amount) FOR quarter IN (' + @columns + N')
) AS pivot_sales;';
-- 步骤3:执行动态SQL
EXEC sp_executesql @sql;
说明:STRING_AGG
函数SQL Server 2017 +支持的,若需兼容低版本,可使用FOR XML PATH
拼接列名的替代方案,如:SELECT @columns = STUFF((SELECT ',' + QUOTENAME(quarter) FROM (SELECT DISTINCT quarter FROM region_sales) AS t FOR XML PATH('')), 1, 1, '')
输出结果:
结果解读:
PIVOT
需要手动修改列名,而动态SQL通过STRING_AGG
自动获取所有行值,无需修改代码;QUOTENAME(quarter)
将列名转换为[Q1]
格式,避免特殊字符(如:空格、数字开头)导致的语法错误;PIVOT
运算符将行数据转换为列数据,简化了交叉报表的生成过程,关键特性如下:
SUM
、COUNT
、AVG
等多种聚合函数,根据业务需求选择;PIVOT
适合行值固定的场景,动态SQL适合行值灵活变化的场景。实际应用中,PIVOT
运算符常用在生成年度销售报表、地区业绩对比表、多维度数据分析等场景,使数据展示更符合我们的阅读习惯。
说到底,这些主流数据库查询语法的差异,更像方言而非外语。在掌握通用SQL查询语句的基础上,我们再有针对性记些 “方言”,如:Oracle的ROWNUM
、PostgreSQL的JSONB
、SQL Server的PIVOT
及MySQL的GROUP_CONCAT
,等等,还有LIMIT
这样的一些细枝末节,就够用了。真遇到生僻场景,我们查手册比死记硬背更高效。
阅读原文:原文链接