Go API调用ClickHouse查询返回空行及Scan错误求助
问题分析与解决办法
1. 错误原因拆解
「sql: Rows are closed」错误
这个问题的核心是**rows.Close()被提前触发**,常见场景:
defer rows.Close()的位置错误:比如在db.Query()报错的分支之后才声明defer,或者在获取rows对象前就写了defer。- 函数在
rows.Next()循环前提前return,导致defer执行关闭操作,后续代码再访问rows就会报错。
「converting driver.Value type *float64 to a float64」错误
这是ClickHouse驱动的类型映射问题:部分版本的驱动会将数值类型以指针形式返回,但你在Scan时传入了值类型的地址,导致类型不匹配。
2. 具体解决方案
修复rows关闭时机
确保defer rows.Close()在成功获取rows对象后立即声明,放在错误处理分支之后:
rows, err := db.QueryContext(ctx, yourSQLQuery) if err != nil { return fmt.Errorf("query failed: %w", err) } defer rows.Close() // 正确位置:仅当rows有效时才延迟关闭
适配驱动的指针类型返回
针对Scan错误,有两种处理方式:
方式一:用指针变量接收后解引用
var value, oldValue float64 for rows.Next() { // 声明指针类型变量接收驱动返回值 var pVal, pOldVal *float64 err := rows.Scan(&pVal, &pOldVal) if err != nil { return fmt.Errorf("scan failed: %w", err) } // 解引用得到实际值(注意判空,避免空指针 panic) if pVal != nil { value = *pVal } if pOldVal != nil { oldValue = *pOldVal } // 你的查询仅返回一行,可直接break跳出循环 break } // 必须检查迭代过程中的错误 if err := rows.Err(); err != nil { return fmt.Errorf("rows iteration error: %w", err) }
方式二:升级ClickHouse驱动
如果使用的是github.com/ClickHouse/clickhouse-go/v2,升级到最新稳定版,新版本修复了部分类型映射的bug,可能直接支持值类型接收。
3. 完整示例代码
package main import ( "context" "database/sql" "fmt" _ "github.com/ClickHouse/clickhouse-go/v2" ) func getTargetValues(ctx context.Context, db *sql.DB) (float64, float64, error) { sqlQuery := "SELECT value, oldValue FROM your_table WHERE [your_condition]" rows, err := db.QueryContext(ctx, sqlQuery) if err != nil { return 0, 0, fmt.Errorf("query failed: %w", err) } defer rows.Close() var value, oldValue float64 for rows.Next() { var pVal, pOldVal *float64 err := rows.Scan(&pVal, &pOldVal) if err != nil { return 0, 0, fmt.Errorf("scan failed: %w", err) } if pVal != nil { value = *pVal } if pOldVal != nil { oldValue = *pOldVal } break } if err := rows.Err(); err != nil { return 0, 0, fmt.Errorf("rows iteration error: %w", err) } return value, oldValue, nil }
内容的提问来源于stack exchange,提问作者liam2682
相关产品推荐
相关产品推荐

