SQL查询案例:多行转换为一行

SQL查询案例:多行转换为一行

使用通常的方式测试表与测试数据

CREATE TABLE TestTitle (

name   VARCHAR(10),

titleVARCHAR(10)

);


INSERT INTO TestTitle VALUES ('张三', '程序员');

INSERT INTO TestTitle VALUES ('张三', '系统管理员');

INSERT INTO TestTitle VALUES ('张三', '网络管理员');

INSERT INTO TestTitle VALUES ('李四', '项目经理');

INSERT INTO TestTitle VALUES ('李四', '系统分析员');


要求

对于测试数据,要求查询结果为:

张三程序员,系统管理员,网络管理员

李四项目经理,系统分析员

这种结构的结果。

思路

简单查看这个结果,很像对字符型的GROUP BY处理。

数值类型的可以SUM,但是字符类型的无法这么处理。

只好依次MAX(1) + MAX(2) + MAX(3)这种办法来处理。

实现

第一步,设置好分组的编号

SELECT

ROW_NUMBER() OVER(PARTITION BY name ORDER BY title) AS no,

name,

title

FROM

TestTitle

ORDER BY

name,

title

no                   name       title

-------------------- ---------- ----------

                   1李四        系统分析员

                   2李四        项目经理

                   1张三        程序员

                   2张三        网络管理员

                   3张三        系统管理员

第二步,根据有编号的子查询,进行分组处理

SELECT

name,

CASE WHEN COUNT(title) = 1 THEN MAX(title)

       WHEN COUNT(title) = 2 THEN

         MAX( CASE WHEN SubQuery.no = 1 THEN title + ',' ELSE '' END )

         + MAX( CASE WHEN SubQuery.no = 2 THEN titleELSE '' END )

       WHEN COUNT(title) = 3 THEN

         MAX( CASE WHEN SubQuery.no = 1 THEN title + ',' ELSE '' END )

         + MAX( CASE WHEN SubQuery.no = 2 THEN title + ','ELSE '' END )

         + MAX( CASE WHEN SubQuery.no = 3 THEN titleELSE '' END )

END AS new_title

FROM

(

SELECT

    ROW_NUMBER() OVER(PARTITION BY name ORDER BY title) AS no,

    name,

    title

FROM

    TestTitle

) subQuery

GROUP BY

name

执行结果

name       new_title

---------- ----------------------------------

李四        系统分析员,项目经理

张三        程序员,网络管理员,系统管理员




对于SQL Server 2005 以上版本使用FOR XML的方式测试表与测试数据要求

与前面的一样

思路

首先把一个用户的数据,单独的读取出来

然后按照分组进行处理

实现

第一步 把一个用户的数据,单独的读取出来

SELECT

',' + title

FROM

TestTitle

WHERE

name = '张三'

FOR XML PATH('')

第二步Group By每个人

SELECT

name,

STUFF(

   (

   SELECT

     ',' + title

   FROM

     TestTitle subTitle

   WHERE

     name = TestTitle.name

   FOR XML PATH('')

   ),

   1, 1, '') AS allTitle

FROM

TestTitle

GROUP BY

name

执行结果

name      allTitle

---------- --------------------------------

李四        项目经理,系统分析员

张三        程序员,系统管理员,网络管理员


 

对于SQL Server 2005 以上版本使用 CTE 的处理方式 (使用递归方式处理)

 

WITH
t1  AS
(
  SELECT
    ROW_NUMBER() OVER(PARTITION BY name ORDER BY title) AS ID,
    name,
    title
  FROM
    TestTitle
),
t2 AS
(
  SELECT 
    t1.id, 
    t1.name, 
    CAST(t1.title AS varchar(100)) AS title
  FROM 
    t1 
  WHERE 
    t1.id = 1
  UNION ALL
  SELECT 
    t1.id, 
    t2.name, 
    CAST( t1.title + ',' + t2.title AS varchar(100)) AS title
  FROM 
    t1, t2
  WHERE 
    t1.name = t2.name
    AND t1.id = (t2.id + 1)
)
SELECT
  name, 
  title
FROM
  t2
WHERE
  NOT EXISTS (
    SELECT 1
    FROM t2 t22
    WHERE
      t2.name = t22.name
      AND t2.id < t22.id
  );

 

name       title

---------- -----------------------------------------------------------
-------------------------------
张三         系统管理员,网络管理员,程序员

李四         项目经理,系统分析员


(2 行受影响)

 

 


对于MySQL使用 GROUP_CONCAT 函数 的方式进行处理(非常简单)

 

mysql> SELECT
    ->   name,
    ->   GROUP_CONCAT(title) AS allTitle
    -> FROM
    ->   TestTitle
    -> GROUP BY
    ->   name;
+------+------------------------------+
| name | allTitle                     |
+------+------------------------------+
| 李四 | 项目经理,系统分析员          |
| 张三 | 程序员,系统管理员,网络管理员 |
+------+------------------------------+
2 rows in set (0.00 sec)

 


 

对于Oracle使用 WMSYS.WM_CONCAT 函数 的方式进行处理(也非常简单)

 

SQL> 
SQL> SELECT
  2    name,
  3    WMSYS.WM_CONCAT(title) AS allTitle
  4  FROM
  5    TestTitle
  6  GROUP BY
  7    name;

NAME
----------
ALLTITLE
-------------------------------------------
李四
项目经理,系统分析员

张三
程序员,系统管理员,网络管理员





 

对于 PostgreSQL 使用 string_agg  函数 的方式进行处理(也非常简单)

 


Test=#

Test=# SELECT

Test-#   name,

Test-#   string_agg(title,',') AS allTitle

Test-# FROM

Test-#   TestTitle

Test-# GROUP BY

Test-#   name;

 name |           alltitle

------+------------------------------

 李四 | 项目经理,系统分析员

 张三 | 程序员,系统管理员,网络管理员

(2 行记录)





对于 DB2 ,也是使用 CTE 递归的方式处理


WITH
t1 (id, name, title) AS
(
  SELECT
    ROW_NUMBER() OVER(PARTITION BY name ORDER BY title) AS ID,
    name,
    title
  FROM
    TestTitle
),
t2 (id, name, title) AS
(
  SELECT 
    t1.id, 
    t1.name, 
    CAST(t1.title AS varchar(100)) AS title
  FROM 
    t1 
  WHERE 
    t1.id = 1
  UNION ALL
  SELECT 
    t1.id, 
    t2.name, 
    CAST( t1.title || ',' || t2.title AS varchar(100)) AS title
  FROM 
    t1, t2
  WHERE 
    t1.name = t2.name
    AND t1.id = (t2.id + 1)
)
SELECT
  name, 
  title
FROM
  t2
WHERE
  NOT EXISTS (
    SELECT 1
    FROM t2 t22
    WHERE
      t2.name = t22.name
      AND t2.id < t22.id
  );

 

NAME       TITLE

---------- ---------------------------------------------------------------------
-------------------------------
SQL0347W  递归公共表表达式 "WZQ.T2" 可能包含无限循环。  SQLSTATE=01605

李四       项目经理,系统分析员

张三       网络管理员,系统管理员,程序员


  已选择 2 条记录,打印 1 条警告消息。


-------------------------------------------------------------------------------------------------------------------------------------------------------


FOR XML PATH 的作用是把结果以xml文本的形式显示出来,也就是说,最终结果就是一个字符串,因此我们就不需要使用什么字符串合并函数了。

STUFF函数的原型是 Stuff(str1, start, len, str2),作用是,删掉str1中start开始的len个字符,用str2替换。因此,可以起到在多个项之间插入分隔符。

比如,


select ',' + name from student for xml path( '' ) group by class

输出结果可能是

,Jim,Kate,Tom,Sally

如果使用STUFF,可以删掉第一个空格


stuff( select ',' + name from student for xml path( '' ) group by class, 1, 1, '' )

如果使用的是hibernate 就要小心,用for xml path 查出来可能会是org.hibernate.lob.SerializableClob,而不是一个字符串,我现在是使用下面的方法转换的
if(value != null && value instanceof org.hibernate.lob.SerializableClob){
					    org.hibernate.lob.SerializableClob clob = (org.hibernate.lob.SerializableClob)value;
					    try {
                            Reader reader = clob.getCharacterStream();
                            BufferedReader br = new BufferedReader(reader);
                            StringBuilder sb = new StringBuilder();
                            String str = null;
                            while((str = br.readLine()) != null){
                                sb.append(str);
                            }
                            _map.put(key.toUpperCase(), sb.toString());
                        } catch (SQLException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
					}
不知道有没有更简单的方法进行配置

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最好的sql教程 轻举技术之“纲”,张合用之“目”,锻造SQL高可用性数据库应用指南从理论到实践,凝聚SQL主流数据库最前沿的技术要领,本书将深入浅出讨论。   本书特色:主要介绍SQL的语法规则及在实际开发中的应用,并且对SQL在MySQL、MS SQL Server、Oracle和DB2中的差异进行了分析;详细讲解数据库对增、删、改、查等SQL的支持并给出了相应的SQL应用案例;透彻分析函数、子查询、表连接、不同DBMS中的SQL语法差异、SQL调优、NULL值处理、事务、开窗函数等高级技术;通过对实际案例开发过程的详细分析,使读者掌握 SQL的综合应用技巧。 内容简介 本书主要介绍SQL的语法规则及在实际开发中的应用,并且对SQL在MySQL、MS SQL Server、Oracle和DB2中的差异进行了分析。本书分为3部分:第1部分为基础篇,主要讲解数据库对增、删、改、查等SQL的支持,给出了这些 SQL的应用案例;第2部分为进阶篇,讲解了函数、子查询、表连接、不同DBMS中的SQL语法差异、SQL调优、NULL值处理、事务、开窗函数等高级技术;第3部分为案例篇,对前两部分的知识进行了综合运用。   本书适合程序开发人员及数据库工程师参考学习,也可作为高等院校相关专业师生的参考书。 目录 第1章 数据库入门  1.1 数据库概述   1.1.1 数据库与数据库管理系统   1.1.2 数据库能做什么   1.1.3 主流数据库管理系统介绍  1.2 数据库基础概念   1.2.1 Catalog   1.2.2 表(Table)   1.2.3 列(Column)   1.2.4 数据类型(DataType)   1.2.5 记录(Record)   1.2.6 主键(PrimaryKey)   1.2.7 索引(Index)   1.2.8 表关联   1.2.9 数据库的语言——SQL   1.2.10 DBA与程序员 第2章 数据表的创建和管理  2.1 数据类型   2.1.1 整数类型   2.1.2 数值类型   2.1.3 字符相关类型   2.1.4 日期时间类型   2.1.5 二进制类型  2.2 通过SQL语句管理数据表   2.2.1 创建数据表   2.2.2 定义非空约束   2.2.3 定义默认值   2.2.4 定义主键   2.2.5 定义外键   2.2.6 修改已有数据表   2.2.7 删除数据表   2.2.8 受限操作的变通解决方案 第3章 数据的增、删、改  3.1 数据的插入   3.1.1 简单的INSERT语句   3.1.2 简化的INSERT语句   3.1.3 非空约束对数据插入的影响   3.1.4 主键对数据插入的影响   3.1.5 外键对数据插入的影响  3.2 数据的更新   3.2.1 简单的数据更新   3.2.2 带WHERE子句的UPDATE语句   3.2.3 非空约束对数据更新的影响   3.2.4 主键对数据更新的影响   3.2.5 外键对数据更新的影响  3.3 数据的删除   3.3.1 简单的数据删除   3.3.2 带WHERE子句的DELETE语句 第4章 数据的检索  4.1 SELECT基本用法   4.1.1 简单的数据检索   4.1.2 检索出需要的列   4.1.3 列别名   4.1.4 按条件过滤   4.1.5 数据汇总   4.1.6 排序  4.2 高级数据过滤   4.2.1 通配符过滤   4.2.2 空值检测   4.2.3 反义运算符   4.2.4 多值检测   4.2.5 范围值检测   4.2.6 低效的“WHERE 1=1”  4.3 数据分组   4.3.1 数据分组入门   4.3.2 数据分组与聚合函数   4.3.3 HAVING语句  4.4 限制结果集行数   4.4.1 MySQL   4.4.2 MS SQL Server 2000   4.4.3 MS SQL Server 2005   4.4.4 Oracle   4.4.5 DB2   4.4.6 数据库分页  4.5 抑制数据重复  4.6 计算字段   4.6.1 常量字段   4.6.2 字段间的计算   4.6.3 数据处理函数   4.6.4 字符串的拼接   4.6.5 计算字段的其他用途  4.7 不从实体表中取的数据  4.8 联合结果集   4.8.1 简单的结果集联合   4.8.2 联合结果集的原则   4.8.3 UNION ALL   4.8.4 联合结果集应用举例 第5章 函数  5.1 数学函数   5.1.1 求绝对值   5.1.2 求指数   5.1.3 求平方根   5.1.4 求随机数   5.1.5 舍入到最大整数   5.1.6 舍入到最小整数   5.1.7 四舍五入   5.1.8 求正弦值   5.1.9 求余弦值   5.1.10 求反正弦值   5.1.11 求反余弦值   5.1.12 求正切值   5.1.13 求反正切值   5.1.14 求两个变量的反正切   5.1.15 求余切   5.1.16 求圆周率π值   5.1.17 弧度制转换为角度制   5.1.18 角度制转换为弧度制   5.1.19 求符号   5.1.20 求整除余数   5.1.21 求自然对数   5.1.22 求以10为底的对数   5.1.23 求幂  5.2 字符串函数   5.2.1 计算字符串长度   5.2.2 字符串转换为小写   5.2.3 字符串转换为大写   5.2.4 截去字符串左侧空格   5.2.5 截去字符串右侧空格   5.2.6 截去字符串两侧的空格   5.2.7 取子字符串   5.2.8 计算子字符串的位置   5.2.9 从左侧开始取子字符串   5.2.10 从右侧开始取子字符串   5.2.11 字符串替换   5.2.12 得到字符的ASCII码   5.2.13 得到一个ASCII码数字对应的字符   5.2.14 发音匹配度  5.3 日期时间函数   5.3.1 日期、时间、日期时间与时间戳   5.3.2 主流数据库系统中日期时间类型的表示方式   5.3.3 取得当前日期时间   5.3.4 日期增减   5.3.5 计算日期差额   5.3.6 计算一个日期是星期几   5.3.7 取得日期的指定部分  5.4 其他函数   5.4.1 类型转换   5.4.2 空值处理   5.4.3 CASE函数  5.5 各数据库系统独有函数   5.5.1 MySQL中的独有函数   5.5.2 MS SQL Server中的独有函数   5.5.3 Oracle中的独有函数 第6章 索引与约束  6.1 索引  6.2 约束   6.2.1 非空约束   6.2.2 唯一约束   6.2.3 CHECK约束   6.2.4 主键约束   6.2.5 外键约束 第7章 表连接  7.1 表连接简介  7.2 内连接(INNER JOIN)  7.3 不等值连接  7.4 交叉连接  7.5 自连接  7.6 外部连接   7.6.1 左外部连接   7.6.2 右外部连接   7.6.3 全外部连接 第8章 子查询  8.1 子查询入门   8.1.1 单值子查询   8.1.2 列值子查询  8.2 SELECT列表中的标量子查询  8.3 WHERE子句中的标量子查询  8.4 集合运算符与子查询   8.4.1 IN运算符   8.4.2 ANY和SOME运算符   8.4.3 ALL运算符   8.4.4 EXISTS运算符  8.5 在其他类型SQL语句中的子查询应用   8.5.1 子查询在INSERT语句中的应用   8.5.2 子查询在UPDATE语句中的应用   8.5.3 子查询在DELETE语句中的应用 第9章 主流数据库的SQL语法差异解决方案  9.1 SQL语法差异分析   9.1.1 数据类型的差异   9.1.2 运算符的差异   9.1.3 函数的差异   9.1.4 常用SQL的差异   9.1.5 取元数据信息的差异  9.2 消除差异性的方案   9.2.1 为每种数据库编写不同的SQL语句   9.2.2 使用语法交集   9.2.3 使用SQL实体对象   9.2.4 使用ORM工具   9.2.5 使用SQL翻译器  9.3 CowNewSQL翻译器   9.3.1 CowNewSQL支持的数据类型   9.3.2 CowNewSQL支持的SQL语法   9.3.3 CowNewSQL支持的函数   9.3.4 CowNewSQL的使用方法 第10章 高级话题  10.1 SQL注入漏洞攻防   10.1.1 SQL注入漏洞原理   10.1.2 过滤敏感字符   10.1.3 使用参数化SQL  10.2 SQL调优   10.2.1 SQL调优的基本原则   10.2.2 索引   10.2.3 全表扫描和索引查找   10.2.4 优化手法  10.3 事务   10.3.1 事务简介   10.3.2 事务的隔离   10.3.3 事务的隔离级别   10.3.4 事务的使用  10.4 自动增长字段   10.4.1 MySQL中的自动增长字段   10.4.2 MS SQL Server中的自动增长字段   10.4.3 Oracle中的自动增长字段   10.4.4 DB2中的自动增长字段  10.5 业务主键与逻辑主键  10.6 NULL的学问   10.6.1 NULL与比较运算符   10.6.2 NULL和计算字段   10.6.3 NULL和字符串   10.6.4 NULL和函数   10.6.5 NULL和聚合函数  10.7 开窗函数   10.7.1 开窗函数简介   10.7.2 PARTITION BY子句   10.7.3 ORDER BY子句   10.7.4 高级开窗函数  10.8 WITH子句与子查询 第11章 案例讲解  11.1 报表制作   11.1.1 显示制单人详细信息   11.1.2 显示销售单的详细信息   11.1.3 计算收益   11.1.4 产品销售额统计   11.1.5 统计销售记录的份额   11.1.6 为采购单分级   11.1.7 检索所有重叠日期销售单   11.1.8 为查询编号   11.1.9 标记所有单内最大销售量  11.2 排序   11.2.1 非字段排序规则   11.2.2 随机排序  11.3 表间比较   11.3.1 检索制作过采购单的人制作的销售单   11.3.2 检索没有制作过采购单的人制作的销售单  11.4 表复制   11.4.1 复制源表的结构并复制表中的数据   11.4.2 只复制源表的结构  11.5 计算字符在字符串中出现的次数  11.6 去除最高分、最低分   11.6.1 去除所有最低、最高值   11.6.2 只去除一个最低、最高值  11.7 与日期相关的应用   11.7.1 计算销售确认日和制单日之间相差的天数   11.7.2 计算两张销售单之间的时间间隔   11.7.3 计算销售单制单日期所在年份的天数   11.7.4 计算销售单制单日期所在月份的第一天和最后一天  11.8 结果集转置   11.8.1 将结果集转置为一行   11.8.2 把结果集转置为多行  11.9 递归查询   11.9.1 Oracle中的CONNECT BY子句   11.9.2 Oracle中的SYS_CONNECT_BY_PATH()函数   11.9.3 My SQL Server和DB2中递归查询 附录A 常用数据库系统的安装和使用  A.1 DB2的安装和使用  A.2 MySQL的安装和使用  A.3 Oracle的安装和使用  A.4 Microsoft SQL Server的安装和使用

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值