ecodeclub / eorm

简单 ORM 框架
Apache License 2.0
191 stars 64 forks source link

分库分表:结果集处理——聚合函数(含 Group By 子句) #191

Closed juniaoshaonian closed 1 year ago

juniaoshaonian commented 1 year ago

仅限中文

使用场景

分组键的选择

● 分组键是shardingkey 如果是shardingkey的情况,意味着一个组的元素只会在一个sql.Rows中,逻辑和batchMerger差不多,一条一条从sqlRows里面拿数据就可以了。我们可以直接复用即可 ● 分组键不是shardingkey 不是shardingkey的情况就比较恶心一点了,意味着一个组的元素可能分布在多个sql.Rows。我们需要拿到全部数据然后根据分组键的信息拿到同一个组的信息,进行merger计算。 这种计算方式很显然如果数据量比较大会导致内存占用过多。这个问题比较难解决。用户可以自己通过控制结果集大小来控制内存大小。对这方面我们不做控制。因为我们无法得知用户的具体一行有多大数据。我们到底是1000条去进行限制还是说2000条。

分组信息的匹配

由于我们需要根据分组键的信息去找到相同组的数据,查询的时候就需要携带上分组键的信息。比如 select actorid,SUM(filmid) group by actorid。这个sql是查演员有多少部电影作品。在merger计算的时候需要通过actorid在sqlRows列表中找到相同actorid的数据。当然这个是针对分组键不是shardingkey的情况。

设计

type GroupByMerger struct {
        groupKeys []string
    }

    func (g GroupByMerger) Merge(ctx context.Context, results []*sql.Rows) (merger.Rows, error) {
        // 将数据拿出,这部分没什么需要特别注意的。和之前差不多

        // 怎样将数据进行保存,我想到的是用map进行保存,key用groupKey,value使用[][]any表示多个需要merger的数据行进行聚合计算的时候就将value作为参数传入聚合函数的实现。
        // 但这个有个问题分组键可能是不同数据类型的,我们如何将他们转化成key呢?
    }
    剩余的rows的next就是设置个游标从保存数据的地方取出数据进行聚合函数计算就行了
flycash commented 1 year ago

但这个有个问题分组键可能是不同数据类型的,我们如何将他们转化成key呢 这个可以考虑使用 ekit 里面的 TreeMap。然后 Key 本身被定义成一个结构体:

type Key struct {
columnValues []any
}

这个 Key 本身会实现 Compare 的方法,那么在 Compare 的时候就直接比较 columnValues 就可以了。也就是类似于从左到右比较每一个元素,比如说最开始是 a.columnValues[0],然后 if a.columnValues[0] > b.columnValues[0] 那么就是 a 比较大。如果全部都相等,那么就相等。

这里会有一个问题,就是代码可能比较呆,因为你在这里也不太能够用泛型。比如说 columnValues 里面的元素可能是基本类型,sql.NullXXX 类型,字节数组类型。不过这部分内容你在排序里面已经处理过一次了,实际上是可以复用的。

你需要小心设计测试用例,要覆盖不同的 GROUP BY 的列的元素类型。还要额外考虑: