SQL实现单列多值拆分至多列,使单Hotel ID对应唯一行
当然可以搞定!这种把同一个Hotel ID的多行混合详情拆分、合并成单行多列的需求,在SQL场景里非常常见,用条件聚合或者PIVOT函数就能轻松实现,我给你具体讲两种实用方法:
先明确原表场景
假设你的原表叫hotel_details,数据大概是这样的(每行对应一个Hotel ID+单个类型的详情值):
| hotel_id | details |
|---|---|
| 1 | 城市:北京 |
| 1 | 国家:中国 |
| 1 | 区域:华北 |
| 2 | 城市:上海 |
| 2 | 国家:中国 |
如果你的details列没有明确的类型前缀(比如直接是“北京”“中国”),那得先通过业务规则或正则匹配给每个值归类类型,下面的方法基于能区分类型的前提展开。
方法1:条件聚合(通用绝大多数SQL数据库)
这是兼容性最强的方法,不管是MySQL、PostgreSQL、SQL Server还是Oracle都能用。核心思路是用CASE WHEN判断每行详情的类型,再通过聚合函数把同一Hotel ID的多行结果合并成一行:
SELECT hotel_id, -- 提取城市值,没有的话返回NULL(可以用COALESCE换成默认值,比如'未知') MAX(CASE WHEN details LIKE '城市:%' THEN SUBSTRING(details FROM 4) END) AS 城市, MAX(CASE WHEN details LIKE '国家:%' THEN SUBSTRING(details FROM 4) END) AS 国家, MAX(CASE WHEN details LIKE '区域:%' THEN SUBSTRING(details FROM 4) END) AS 区域 FROM hotel_details GROUP BY hotel_id;
小解释:
CASE WHEN负责识别当前行的详情类型,并提取对应的值MAX()(或者MIN()也可以,因为每个Hotel ID对应每种类型只会有一行)用来把多行的结果“收拢”到一行GROUP BY hotel_id保证最终每个Hotel ID只输出一行
如果你的表本身就有单独的detail_type列(比如专门标记是“城市”“国家”),那代码会更简单:
SELECT hotel_id, MAX(CASE WHEN detail_type = '城市' THEN details END) AS 城市, MAX(CASE WHEN detail_type = '国家' THEN details END) AS 国家, MAX(CASE WHEN detail_type = '区域' THEN details END) AS 区域 FROM hotel_details GROUP BY hotel_id;
方法2:使用PIVOT函数(适用于SQL Server、Oracle等)
如果你的数据库支持PIVOT语法(比如SQL Server、Oracle),可以用更简洁的写法:
以SQL Server为例,先把details拆成类型和值,再用PIVOT转成列:
SELECT * FROM ( -- 先从details里拆分出类型和对应的值 SELECT hotel_id, LEFT(details, CHARINDEX(':', details)-1) AS detail_type, RIGHT(details, LEN(details)-CHARINDEX(':', details)) AS detail_value FROM hotel_details ) AS source_data PIVOT ( -- 聚合值,确保每个类型只取一个 MAX(detail_value) -- 指定要转成列的类型 FOR detail_type IN ([城市], [国家], [区域]) ) AS pivot_result;
注意点:
- 如果某些Hotel ID缺少某类信息,对应的列会显示
NULL,可以用COALESCE(MAX(...), '未知')替换成你想要的默认值 - 如果
details的类型标识不清晰,得先做数据清洗,比如用正则匹配关键词来归类(比如包含“市”的归为城市,包含“国”的归为国家等)
内容的提问来源于stack exchange,提问作者Berenike
相关产品推荐
相关产品推荐

