有一个非常高级的SQL查询的问题

时间:2013-04-10 15:19:02

标签: mysql sql dynamicquery

我需要在SQL中进行高级选择,但是我被卡住了。

我有下表:

id | user_id | position | value
1  |   1     |    1     |   1
1  |   1     |    2     |   1
1  |   1     |    3     |   3
1  |   2     |    1     |   2
1  |   2     |    2     |   2
1  |   2     |    3     |   2
1  |   3     |    1     |   3
1  |   3     |    2     |   2
1  |   3     |    3     |   1

我需要一个查询,它给我一个按此命令的结果集:

  1. 每个用户的总和(用户1:5,用户2:6,用户3:6)
  2. 每个用户的位置3的值(用户1:3,用户2:2,用户3:1)
  3. 每个用户的pos 3 + val for pos 2(用户1:4,用户2:4,用户3:4)
  4. 用于pos 3的val +用于每个用户的pos 2 + val用于pos 1(用户1:5,用户2:6,用户3:6)
  5. 这只是一个例子,该表实际上可以包含更多的位置,所以我需要一个在三个位置上没有硬编码的查询。

    注意:每个user_id的位置始终相同。在这个例子中它是三个,但我也可以截断表并使用五个位置为每个用户添加数据。

    一个丑陋的解决方案是假设永远不会超过十个位置,创建pos1,pos2等作为列,并在查询中相应地添加它们。如果你只使用三个位置就会获得很多NULL值,而你最多也会遇到10个位置。

    我考虑过使用临时表,但也没有找到突破。

    你会怎么做?

2 个答案:

答案 0 :(得分:1)

您可以执行以下操作:

select user_id
    , sum(value) as value_sum
    , (select value from my_table where user_id = t.user_id and position = 3) as pos_3_val
    , (select sum(value) from my_table where user_id = t.user_id and position >= 2) as pos_2_3_val
    , (select sum(value) from my_table where user_id = t.user_id and position >= 1) as pos_1_2_3_val
from my_table as t
group by user_id
order by user_id

我认为这应该适用于大多数RDBMS。

如果必须通过动态,您可以在存储过程或应用程序中创建此查询并运行它。

您还可以动态地从以下查询中转移结果:

select *
    , (
        select sum(value)
        from my_table
        where user_id = t.user_id
            and position >= t.position
    ) as running_total_descending
from my_table t

如果有任何问题,请告诉我们,如果您在创建动态版本时遇到问题(以及哪个RDBMS)。

<强>更新

现在我们知道了RDBMS(MySQL),我们可以拥有一个特定的动态版本:

set @sql = null;
select
  group_concat(distinct
    concat(
      ' sum(case when position >= ',
      position,
      ' then value end) as pos_',
      position,
      '_plus'
    )
  ) into @sql
from my_table;

set @sql = concat('select user_id,', @sql, ' from my_table t group by user_id;');

prepare stmt from @sql;
execute stmt;
deallocate prepare stmt;

<强> SQL Fiddle

特别感谢@bluefeet经常发布此类解决方案。

我还应该注意到许多开发人员认为这种类型的旋转通常属于应用程序或前端。我也不例外,因为你的应用程序通常可以比OLTP数据库更好地扩展。

答案 1 :(得分:1)

  

我需要一个在三个位置上没有硬编码的查询。

然后您无法在中输出小计。 SQL要求在准备查询时修复列;您无法编写一个动态追加更多列的查询,因为它会发现数据中有多少个不同的值。

但是,您可以输出动态数量的

SELECT t1.user_id, CONCAT(t1.position, '-', MAX(t2.position)) AS position_range, 
  SUM(t2.value) AS subtotal
FROM MyTable t1
INNER JOIN MyTable t2
  ON t1.user_id = t2.user_id AND t1.position <= t2.position
GROUP BY t1.user_id, t1.position;

输出结果为:

+---------+----------------+----------+
| user_id | position_range | subtotal |
+---------+----------------+----------+
|       1 | 1-3            |        5 |
|       1 | 2-3            |        4 |
|       1 | 3-3            |        3 |
|       2 | 1-3            |        6 |
|       2 | 2-3            |        4 |
|       2 | 3-3            |        2 |
|       3 | 1-3            |        6 |
|       3 | 2-3            |        3 |
|       3 | 3-3            |        1 |
+---------+----------------+----------+

在获取整个结果集后,您必须编写应用程序代码以将其转换为列。

抱歉,无法在任何品牌的RDBMS中编写完全动态的数据透视查询。你有两个选择:

  1. 编写代码以根据数据生成SQL,如@ TimLehner的更新答案所示

  2. 编写代码以对上面显示的通用查询进行后处理。