You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用gorm-clickhouse读取ClickHouse的WITH TOTALS行?

通过GORM获取ClickHouse WITH TOTALS数据的可行方案

由于GORM本身没有封装ClickHouse的WITH TOTALS特性,但可以结合底层的clickhouse-go驱动能力实现需求,同时支持用map[string]interface{}接收主结果和总计数据,具体方案如下:

核心思路:Raw查询+底层Rows解析

用GORM执行带WITH TOTALS的原生SQL,将返回的Rows对象类型断言为clickhouse-go的Rows,利用其自带的HasTotals()和Totals()方法提取总计数据,再手动解析为map[string]interface{}格式。

代码示例

package main

import (
	"context"
	"github.com/ClickHouse/clickhouse-go/v2"
	"gorm.io/driver/clickhouse"
	"gorm.io/gorm"
)

func main() {
	ctx := context.Background()
	// 初始化GORM连接ClickHouse
	db, err := gorm.Open(clickhouse.New(clickhouse.Config{
		DSN: "tcp://localhost:9000?database=default&username=default&password=",
	}), &gorm.Config{})
	if err != nil {
		panic(err)
	}

	// 执行带WITH TOTALS的原生查询
	sql := "SELECT category, count(id) AS cnt FROM orders GROUP BY category WITH TOTALS"
	rows, err := db.Raw(sql).Rows()
	if err != nil {
		panic(err)
	}
	defer rows.Close()

	// 类型断言为clickhouse-go的Rows对象,获取底层驱动能力
	chRows, ok := rows.(*clickhouse.Rows)
	if !ok {
		panic("无法将rows转换为clickhouse.Rows类型")
	}

	// 获取查询列名,用于构造map的key
	cols, err := chRows.Columns()
	if err != nil {
		panic(err)
	}

	// 解析主结果集到map切片
	var mainResults []map[string]interface{}
	for chRows.Next() {
		// 初始化值容器
		values := make([]interface{}, len(cols))
		for i := range values {
			values[i] = new(interface{})
		}
		// 扫描当前行数据
		if err := chRows.Scan(values...); err != nil {
			panic(err)
		}
		// 构造单条结果map
		item := make(map[string]interface{}, len(cols))
		for i, col := range cols {
			item[col] = *(values[i].(*interface{}))
		}
		mainResults = append(mainResults, item)
	}

	// 解析总计数据到map
	var totals map[string]interface{}
	if chRows.HasTotals() {
		totalsRow := chRows.Totals()
		values := make([]interface{}, len(cols))
		for i := range values {
			values[i] = new(interface{})
		}
		if err := totalsRow.Scan(values...); err != nil {
			panic(err)
		}
		totals = make(map[string]interface{}, len(cols))
		for i, col := range cols {
			totals[col] = *(values[i].(*interface{}))
		}
	}

	// 输出结果示例
	println("主结果:")
	for _, item := range mainResults {
		println(item["category"], item["cnt"])
	}
	println("\n总计:")
	if totals != nil {
		println(totals["category"], totals["cnt"])
	}
}

关键注意事项

  • 必须使用github.com/ClickHouse/clickhouse-go/v2版本的驱动,GORM的clickhouse驱动依赖该版本,低版本可能不支持Rows.Totals()方法。
  • 类型断言需确保GORM连接的是ClickHouse数据库,实际代码中建议用更温和的错误处理替代panic。
  • 操作完成后务必关闭Rows对象,避免数据库连接泄漏。

内容的提问来源于stack exchange,提问作者zzz TDT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 19:16:30