zoukankan      html  css  js  c++  java
  • 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,而不是一个字符串,我现在是使用下面的方法转换的
    [java] view plain copy
     
    1. if(value != null && value instanceof org.hibernate.lob.SerializableClob){  
    2.                         org.hibernate.lob.SerializableClob clob = (org.hibernate.lob.SerializableClob)value;  
    3.                         try {  
    4.                             Reader reader = clob.getCharacterStream();  
    5.                             BufferedReader br = new BufferedReader(reader);  
    6.                             StringBuilder sb = new StringBuilder();  
    7.                             String str = null;  
    8.                             while((str = br.readLine()) != null){  
    9.                                 sb.append(str);  
    10.                             }  
    11.                             _map.put(key.toUpperCase(), sb.toString());  
    12.                         } catch (SQLException e) {  
    13.                             e.printStackTrace();  
    14.                         } catch (IOException e) {  
    15.                             e.printStackTrace();  
    16.                         }  
    17.                     }  
    不知道有没有更简单的方法进行配置
  • 相关阅读:
    福大软工 · 第十次作业
    最终作业:软工实践个人总结
    第三视角Beta答辩总结
    Beta 冲刺(7/7)
    Beta 冲刺 (6/7)
    Beta 冲刺 (5/7)
    Beta 冲刺 (4/7)
    Beta 冲刺 (3/7)
    Beta 冲刺 (2/7)
    福大软工 · 第十次作业
  • 原文地址:https://www.cnblogs.com/zhangdl/p/7851929.html
Copyright © 2011-2022 走看看