2

我需要在 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. Val for pos 3 + val for pos 2 每个用户(用户 1:4,用户 2:4,用户 3:4)
  4. 位置 3 的值 + 位置 2 的值 + 位置 1 的值 每个用户(用户 1:5,用户 2:6,用户 3:6)

这只是一个例子,表实际上可以包含更多的位置,所以我需要一个没有硬编码在三个位置上的查询。

注意:每个 user_id 总是有相同数量的位置。在此示例中,它是三个,但我也可以截断表格并使用五个位置为每个用户添加数据。

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

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

你会怎么做?

4

2 回答 2

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小提琴

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

我还应该指出,许多开发人员认为这种类型的旋转通常属于应用程序或前端。我也不例外,这既是为了分离关注点,也是因为您的应用程序通常可以比您的 OLTP 数据库更好地扩展。

于 2013-04-10T15:27:14.483 回答
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. 编写代码来对我上面展示的通用查询进行后处理。

于 2013-04-10T21:06:06.707 回答