Sql 数学!近似平均值,不存储整个数据集

Sql 数学!近似平均值,不存储整个数据集,sql,math,average,Sql,Math,Average,显而易见(但昂贵)的解决方案: TrackID Rating NumberOfVotes 我想将轨道(1-10)的评级存储在如下表格中: TrackID Vote 然后是一个简单的 SELECT AVERAGE(Vote) FROM `table` where `TrackID` = some_val 计算平均值 然而,我担心这方面的可伸缩性,尤其是每次都需要重新计算 提出但可能愚蠢的解决方案: TrackID Rating NumberOfVotes 每次有人投票时,评分将更新为 ne

显而易见(但昂贵)的解决方案:

TrackID
Rating
NumberOfVotes
我想将轨道(1-10)的评级存储在如下表格中:

TrackID
Vote
然后是一个简单的

SELECT AVERAGE(Vote) FROM `table` where `TrackID` = some_val
计算平均值

然而,我担心这方面的可伸缩性,尤其是每次都需要重新计算

提出但可能愚蠢的解决方案:

TrackID
Rating
NumberOfVotes
每次有人投票时,
评分将更新为

new_rating = ((old_rating * NumberOfVotes) + vote) / (NumberOfVotes + 1)
并存储为
TrackID
的新
评级值。现在,无论何时需要
评级
,它都是一个简单的查找,而不是计算

显然,这并不计算平均数。我尝试了一些小数据集,它接近平均值。我相信它可能会随着数据集的增加而收敛?但我担心这可能会产生分歧


你们觉得怎么样?谢谢

您的解决方案完全合法。与从完整源代码集中计算的值相比,它的浮点精度仅相差几倍左右

假设您有无限的数值精度,该计算会正确更新平均值。实际上,您可能正在使用整数类型,因此它并不精确

存储累积投票计数和投票数如何?(即,
total=total+vote
numVotes=numVotes+1
)。这样,你就可以通过把一个除以另一个得到准确的平均值


只有当您获得过多的投票,以至于超出了所使用数据类型的范围时,这种方法才会失效。因此,请使用大数据类型(32位应该足够了,除非您期望获得约40亿张选票)

在表中存储
TrackId
RatingSum
numberofvoices

每次有人投票

NumberOfVotes = NumberOfVotes + 1
SumOfVotes = SumOfVotes + ThisVote
  • NumberOfVoces=NumberOfVoces+1
  • RatingsSum=RatingsSum+[用户提供的额定值]
那么在选择

SELECT TrackId, RatingsSum / NumberOfVotes FROM ...

对您的解决方案的小改进。你有一张桌子:

TrackID
SumOfVotes
NumberOfVotes
当有人投票时

NumberOfVotes = NumberOfVotes + 1
SumOfVotes = SumOfVotes + ThisVote
要查看平均值,只需进行除法:

SELECT TrackID, (SumOfVotes/NumberOfVotes) AS Rating FROM `table` 
我想补充一点,在计算平均值时,原始(明显且昂贵)解决方案仅比提供的解决方案昂贵。 增加、删除或更改投票时更便宜。 我猜原来的桌子

TrackID
Vote
VoterID
仍然需要在提供的解决方案中使用,以跟踪每个选民的投票(评级)。因此,此表中的每一项更改(插入、删除或投票更新)都必须更新两个表


换句话说,原始解决方案可能是最好的方法。

您当然可以在不掌握所有数据点的情况下计算连续平均值和标准偏差。您只需要累加总和、平方和和点数

这不是一个近似值;平均值和标准偏差是精确的

下面是一个Java类,它演示了。您可以根据需要调整SQL解决方案:

package statistics;

public class StatsUtils
{
    private double sum;
    private double sumOfSquares;
    private long numPoints;

    public StatsUtils()
    {
        this.init();
    }

    private void init()
    {
        this.sum = 0.0;
        this.sumOfSquares = 0.0;
        this.numPoints = 0L;
    }

    public void addValue(double value)
    {
        // Check for overflow in either number of points or sum of squares; reset if overflow is detected
        if ((this.numPoints == Long.MAX_VALUE) || (this.sumOfSquares > (Double.MAX_VALUE-value*value)))
        {
            this.init();
        }

        this.sum += value;
        this.sumOfSquares += value*value;
        ++this.numPoints;
    }

    public double getMean()
    {
        double mean = 0.0;

        if (this.numPoints > 0)
        {
            mean = this.sum/this.numPoints;
        }

        return mean;
    }

    public double getStandardDeviation()
    {
        double standardDeviation = 0.0;

        if (this.numPoints > 1)
        {
            standardDeviation = Math.sqrt((this.sumOfSquares - this.sum*this.sum/this.numPoints)/(this.numPoints-1L));
        }

        return standardDeviation;
    }

    public long getNumPoints() { return this.numPoints; }
}

现在你提到这件事真是太明显了!谢谢,奥利:-)