【问题标题】:Select avg vote but leave out the top and bottom 5% of votes using mysql使用 mysql 选择 avg vote 但忽略前 5% 和后 5% 的投票
【发布时间】:2013-01-01 10:11:28
【问题描述】:

我有一张带票的桌子。投票只有数字(没有文字,没有是/否等)。

原始数据示例

id   | question_id | vote
1    | 63          | 80
2    | 63          | 13
3    | 63          | 992
...
100  | 63          | 900000
101  | 63          | 5

我想获得特定问题的平均值,但忽略最高和最低 X% 的选票(例如:忽略 5% - 所以每 100 票我将平均只得到 90 票) - 不是按 id 而是通过投票数的最大值和最小值。

在上表中,900000 和 5 不会在平均值中考虑(假设它们是该问题的最高票和最低票)

【问题讨论】:

  • 您为什么要这样做?你认为它会消除偏见吗?
  • @WaleedKhan 我认为它是一种有效的方法。
  • 这可能很费力,但如果您不怕子查询,应该很容易做到。
  • @WaleedKhan ,是的,它会消除偏见和只投票高或低数字的人。
  • @JanDvorak 喜欢看看如何做到这一点......顺便说一句,每次我拉这样的东西时它会杀死服务器吗?谢谢!

标签: mysql select average


【解决方案1】:

这个查询应该可以完成这项工作 (see example on SQL Fiddle):

SELECT AVG(vote) FROM
(
  SELECT vote, @r:=@r+1 AS rownum
  FROM votes, (SELECT @r:=0) x
  WHERE question_id = 63
  ORDER BY vote
) x
WHERE rownum > @r * .05
  AND rownum <= @r * .95
;

子查询使用变量为每条记录分配一个行号,外层查询删除最低和最高的数字。

【讨论】:

  • 看来你在渐近复杂度和代码长度方面打败了我。我怎么这么讨厌本地的@variables?
  • 我怀疑我比你更喜欢变量,但如果没有分析函数,它们有时会提供最易读和最快的解决方案......
  • @PeterLang 绝对惊人!非常感谢。顺便说一句,当我进行此查询时,它会给服务器带来压力吗?
【解决方案2】:

这是您可能使用的基本思想:

SELECT AVG(vote) AS average
FROM tbl o 
WHERE question_id=?
AND (
   SELECT count(*)
   FROM tbl i
   WHERE o.question_id = i.question_id
   AND o.vote < i.vote 
)/(
   SELECT count(*)
   FROM tbl inner
   WHERE o.question_id = i.question_id
) IS BETWEEN 0.05 AND 0.95
GROUP BY question_id

直观地说,复杂度是投票数的二次方(除非查询执行器可以优化第一个子查询;第二个更容易优化)

其他一些数据库引擎有一个行号作为您可以在查询中使用的值。这可能会帮助您实现线性复杂性。

您也可以通过多个查询实现线性复杂性:

  1. 用问题ID查询总行数。
  2. 按排序顺序计算 5% 和 95% 边界的位置。
  3. 用LIMIT 查询与边界对应的投票。感谢UNION,您可以并行运行两个查询。
  4. 查询知道票数最高和最低票的平均票数。

如果有重复投票,您可能需要将vote 列替换为vote, ID 向量,以便找到边界(这适用于两个查询)。基本思路是一样的。

【讨论】:

    猜你喜欢
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多