首页 > 解决方案 > 在 Go 中实现聚合的最佳方式(如 SQL 中的 GROUP BY)?

问题描述

假设我有一个结构

type row struct {
    f1, f2, f3 string
    v int64
}

我们可以把它想象成表格中的一行。

另外,我需要实现一个像这个查询一样进行聚合的函数:

SELECT f1, f2, f3, SUM(v) FROM table GROUP BY f1, f2, f3

所以,我必须实现功能:

type key struct {
    f1, f2, f3 string
}
func aggregate(t []row) map[key]int64

或者如果可以

func aggregate(t []row) map[string]row

例如,其中映射键是 f1+f2+f3

func aggregate(t []row)  []row

如果结果将包含唯一的 f1、f2、f3 组合(DISTINCT f1、f2、f3),也可以使用

我有两种变体:

func aggregate1(t []row) map[key]int64 {
    res := map[key]int64{}
    for _, r := range t {
        res[key{r.f1, r.f2, r.f3}] += r.v
    }
    return res
}
func aggregate2(t []row) map[string]*row {
    res := map[string]*row{}
    for _, r := range t {
        var sb strings.Builder
        sb.WriteString(r.f1)
        sb.WriteString("#")
        sb.WriteString(r.f2)
        sb.WriteString("#")
        sb.WriteString(r.f3)
        id := sb.String()
        t := res[id]
        if t == nil {
            t = &row{f1: r.f1, f2: r.f2, f3: r.f3, v: 0}
            res[id] = t
        }
        t.v += r.v
    }
    return res
}

第一个变种在https://golang.org/pkg/runtime/?m=all#mapassign (runtime.mapassign)中花费了太多时间

第二种变体的想法是使用更快的https://golang.org/pkg/runtime/?m=all#mapassign_faststr (runtime.mapassign_faststr),但是 strings.Builder.WriteString 消除了 runtime.mapassign_faststr 的所有好处 :(

那么,您能否提出更多关于如何实现此聚合的想法?

我正在考虑如何有效地计算第二个变体中的“id”。它应该是独一无二的。我的变体是独一无二的,因为 f1、f2 和 f3 不能包含“#”字符。

标签: go

解决方案


推荐阅读