【问题标题】:How can I turn a bunch of rows into aggregated columns WITHOUT using pivot in SQL Server 2005?如何在不使用 SQL Server 2005 中的数据透视的情况下将一堆行转换为聚合列?
【发布时间】:2010-04-27 16:46:39
【问题描述】:

这是场景: 我有一个记录 user_id、module_id 和查看模块的日期/时间的表。

例如。

Table: Log
------------------------------
User_ID  Module_ID   Date
------------------------------
1       red         2001-01-01
1       green       2001-01-02
1       blue        2001-01-03
2       green       2001-01-04
2       blue        2001-01-05
1       red         2001-01-06
1       blue        2001-01-07
3       blue        2001-01-08
3       green       2001-01-09
3       red         2001-01-10
3       green       2001-01-11
4       white       2001-01-12

我需要获得一个以 user_id 作为第一列的结果集,然后是每个模块的列。行数据就是 user_id 和用户查看每个模块的次数。

例如。

---------------------------------
User_ID  red green   blue    white
---------------------------------
1       2   1       2       0
2       0   1       1       0
3       1   2       1       0
4       0   0       0       1

我最初认为我可以用 PIVOT 做到这一点,但没有骰子;该数据库是在 SQL Server 2005 中运行的转换后的 SQL Server 2000 数据库。我无法更改兼容性级别,因此 pivot 已失效。

另一个问题是模块会有所不同,每次添加或删除模块时都重新编写查询是不可行的。这意味着我不能在模块中进行硬编码,因为我事先不知道哪些会被安装,哪些不会被安装。

我怎样才能做到这一点?

【问题讨论】:

    标签: sql sql-server sql-server-2005 pivot


    【解决方案1】:

    可以使用 CASE 和 GROUP BY 模拟 PIVOT

    select
        [user_id],
        sum(case when [Module_ID] = 'red' then 1 else 0 end) as red,
        sum(case when [Module_ID] = 'green' then 1 else 0 end) as green,
        sum(case when [Module_ID] = 'blue' then 1 else 0 end) as blue,
        sum(case when [Module_ID] = 'white' then 1 else 0 end) as white
    from [log]
    group by
        [user_id]
    

    当然,如果模块不同(如问题中所述),这将不起作用,但是 PIVOT 也有同样的问题。

    动态生成一些 sql 解决了这个问题,但是这个解决方案有点臭!

    declare @sql nvarchar(max)
    
    set @sql = '
    select
        [user_id],'
    
    select @sql = @sql + '
        sum(case when [Module_ID] = ''' + replace([Module_ID], '''','''''') + ''' then 1 else 0 end) as [' + replace([Module_ID], '''','') + '],'
    from (select distinct [Module_ID] from [log]) as moduleids
    
    set @sql = substring(@sql,1,len(@sql)-1) + '
    from [log]
    group by
        [user_id]
    '
    print @sql
    exec sp_executesql @sql
    

    请注意,如果模块 id 数据不可信,这可能容易受到 sql-injection 的攻击。

    【讨论】:

    • 有没有办法让 sum/case 语句块动态化?安装的模块不是固定的,所以模块的离散枚举是不可行的。
    • 在您发布之前发现了这一点。 PIVOT 也有同样的问题 - 你必须预先硬编码你想要的列。
    • 有一种动态方式来获取列,如下所示:stackoverflow.com/questions/2344590/… 但由于无法使用枢轴,这对我不起作用。有没有办法将两者结合起来?
    • 是的,可以像我编辑的答案一样动态生成 sql,但我不喜欢它!
    【解决方案2】:
    SELECT User_ID, MAX(red) AS red, MAX(green) AS green, MAX(blue) AS blue,
      MAX(white) AS white FROM
    ((SELECT User_ID, COUNT(Module_ID) AS red, 0 AS green, 0 AS blue,
      0 AS white
    FROM log
    WHERE Module_ID = 'red'
    GROUP BY User_ID)
    UNION
    (SELECT User_ID, 0 AS red, COUNT(Module_ID) AS green, 0 AS blue,
      0 AS white
    FROM log
    WHERE Module_ID = 'green'
    GROUP BY User_ID)
    UNION
    (SELECT User_ID, 0 AS red, 0 AS green, COUNT(Module_ID) AS blue,
      0 AS white
    FROM log
    WHERE Module_ID = 'blue'
    GROUP BY User_ID)
    UNION
    (SELECT User_ID, 0 AS red, 0 AS green, 0 AS blue,
      COUNT(Module_ID) AS white
    FROM log
    WHERE Module_ID = 'white'
    GROUP BY User_ID))
    GROUP BY User_ID
    ORDER BY User_ID
    

    【讨论】:

    • 安装的模块不是固定的,所以模块的离散枚举是不可行的。有没有动态的方法来做到这一点?
    【解决方案3】:

    使用 MySQL 我这样做了:

    1. 将您的数据复制到 Log_Table.sql
    2. create table Log (User_ID mediumint, Module_ID CHAR(5), dte CHAR(10));
    3. load data infile 'Log_Table.sql' INTO TABLE Log FIELDS TERMINATED BY ',';
    4. 枢轴:

      select User_ID AS 'USER',  sum(case
      Module_ID WHEN 'red'   then 1 else 0
      END) AS 'red', 
      
      sum(case Module_ID WHEN 'green' then 1
      else 0 END) AS 'green', 
      
      sum(case Module_ID WHEN 'blue'  then 1
      else 0 END) AS 'blue', 
      
      sum(case Module_ID WHEN 'white' then 1
      else 0 END) AS 'white'
      
      from Log 
      
      Group By User_ID;
      
      > +------+------+-------+------+-------+ 
      > | USER | red  | green | blue | white |
      > +------+------+-------+------+-------+ 
      > |    1 |    2 |     1 |    2 |     0 |
      > |    2 |    0 |     1 |    1 |     0 |
      > |    3 |    1 |     2 |    1 |     0 |
      > |    4 |    0 |     0 |    0 |     1 |
      > +------+------+-------+------+-------+ 
      > 4 rows in set (0.00 sec)
      

    希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      我相信characteristic functions 是你想要的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-09-05
        • 2022-12-03
        • 2023-01-03
        • 2013-04-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多