【问题标题】:SUM from the results of a subquery of N results as max for each user来自 N 个结果的子查询结果的 SUM 作为每个用户的最大值
【发布时间】:2017-01-26 15:32:30
【问题描述】:

让我们假设这个架构:

CREATE TABLE test
(
test_Id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
user_Id INT NOT NULL,
date DATE,
result VARCHAR(255) NOT NULL,
) engine=innodb;

我的目标是为每个不同的 user_Id 提取最后 5 个结果作为最大值,从最新到最旧排序。除此之外,根据结果列,我想计算这些最后结果的比率,以便能够选择具有最佳比率的 3 个用户。

所以我们以这个数据为例:

test_Id | user_Id | date      | result
1       | 1       |2016-09-05 | A
2       | 3       |2016-09-13 | A
3       | 3       |2016-09-30 | A
4       | 4       |2016-09-22 | A
5       | 4       |2016-09-11 | C
6       | 7       |2016-09-18 | D
7       | 4       |2016-09-08 | B
8       | 6       |2016-09-20 | E
9       | 7       |2016-09-16 | A
10      | 7       |2016-09-29 | E
11      | 7       |2016-09-23 | A
12      | 7       |2016-09-16 | B
13      | 4       |2016-09-15 | B
14      | 7       |2016-09-07 | C
15      | 7       |2016-09-09 | A
16      | 3       |2016-09-26 | A
17      | 4       |2016-09-11 | C
18      | 4       |2016-09-30 | E

我能够实现的是这个查询:

SELECT p.user_Id, p.RowNumber, p.date, p.result, 
      SUM(CASE WHEN p.result='A' OR p.result='B' 
      THEN 1 ELSE 0 END) as avg
FROM (
    SELECT  @row_num := IF(@prev_value=user_Id,@row_num+1,1) 
          AS RowNumber, test_Id, user_Id, date, result, 
          @prev_value := user_Id
    FROM test,
    (SELECT @row_num := 1) x,
    (SELECT @prev_value := '') y
    WHERE @prev_value < 5
    ORDER BY user_Id, YEAR(date) DESC, MONTH(date) DESC, 
             DAY(date) DESC
) p
WHERE p.RowNumber <=10
GROUP BY p.user_Id, p.test_Id
ORDER BY p.user_Id, p.RowNumber;

这个查询为我提供了这种输出:

 RowNumber |test_Id | user_Id | date      | result | avg
 1         | 1      | 1       |2016-09-05 | A      | 1
 1         | 3      | 3       |2016-09-30 | A      | 1
 2         | 16     | 3       |2016-09-26 | A      | 1
 3         | 2      | 3       |2016-09-13 | A      | 1
 1         | 18     | 4       |2016-09-30 | E      | 0
 2         | 4      | 4       |2016-09-22 | A      | 1
 3         | 13     | 4       |2016-09-15 | B      | 1
 4         | 5      | 4       |2016-09-11 | C      | 0
 5         | 17     | 4       |2016-09-11 | C      | 0
 1         | 8      | 6       |2016-09-20 | E      | 0
 1         | 10     | 7       |2016-09-29 | E      | 0
 2         | 11     | 7       |2016-09-23 | A      | 1
 3         | 6      | 7       |2016-09-18 | D      | 0
 4         | 9      | 7       |2016-09-16 | A      | 1
 5         | 12     | 7       |2016-09-16 | B      | 1

我所期望的是,在 avg 列中将获得每个用户匹配条件(A 或 B 值)的总结果,以便能够从每个 user_id 的 5 个结果中计算比率。 (0, 0.2, 0.4, 0.6, 0.8, 1)。 像这样的:

 RowNumber |test_Id | user_Id | date      | result | avg
 1         | 1      | 1       |2016-09-05 | A      | 1
 1         | 3      | 3       |2016-09-30 | A      | 3
 2         | 16     | 3       |2016-09-26 | A      | 3
 3         | 2      | 3       |2016-09-13 | A      | 3
 1         | 18     | 4       |2016-09-30 | E      | 2
 2         | 4      | 4       |2016-09-22 | A      | 2
 3         | 13     | 4       |2016-09-15 | B      | 2
 4         | 5      | 4       |2016-09-11 | C      | 2
 5         | 17     | 4       |2016-09-11 | C      | 2
 1         | 8      | 6       |2016-09-20 | E      | 0
 1         | 10     | 7       |2016-09-29 | E      | 3
 2         | 11     | 7       |2016-09-23 | A      | 3
 3         | 6      | 7       |2016-09-18 | D      | 3
 4         | 9      | 7       |2016-09-16 | A      | 3
 5         | 12     | 7       |2016-09-16 | B      | 3

在进行 SUM 时,我是否受到 GROUP BY p.user_Id, p.test_Id 子句的限制?我尝试了仅使用 user_Id 作为 GROUP BY 子句且仅使用 test_Id 作为 GROUP BY 子句的查询,但没有得到预期的结果。

【问题讨论】:

  • 你的 group by 一定是错的,见stackoverflow.com/a/39551434
  • 使用 MySQL 5.5 版本。也许您正确地指出了“结果”是一个非“分组依据”列。无论如何,我喜欢@P.Salmon 方法。
  • 您有一个由 2 列组成的分组,而有 4 个非 agg 列。这会导致垃圾数据结果
  • 另外,以下解决方案不能保证有效。手册声明它是为了安全优先Here,并且在将这样的东西投入生产之前,人们经常会从男爵Here 那里阅读关于它的必修圣经

标签: mysql subquery mysql-variables


【解决方案1】:

我认为你需要计算平均值然后加入

select  a.rn,a.test_id,a.user_id,a.date,a.result,u.avg from
(
select      t1.*
        ,   if (t1.user_id <> @p, @rn:=1,@rn:=@rn+1) rn
        ,   @p:=t1.user_id p
from        (select @rn:=0, @p:='') rn,test t1
order   by t1.user_id, t1.date desc
) a 
join
(
select  s.user_id
            , sum(case when s.result  = 'A' or s.result = 'B' then 1 else 0 end) as avg
from
(
select      t1.*
        ,   if (t1.user_id <> @p, @rn:=1,@rn:=@rn+1) rn
        ,   @p:=t1.user_id p

from        (select @rn:=0, @p:='') rn,test t1
order   by t1.user_id, t1.date desc
) s
where s.rn <= 5
group   by s.user_id 
) u on u.user_id = a.user_id
where   a.rn <= 5

【讨论】:

  • 这种方法符合我的目标,而且性能非常好。我做了一些修改以获得比率和订单结果,这取决于它以满足我的需求。非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多