Golang动态SQL创建与导入实现指南
Golang动态SQL创建与导入实现指南 我们从客户那里收到Excel文件。 我们不知道Excel文件的列名。
我们需要根据Excel列名在运行时创建表。 将Excel导入到MySQL中。
我们在SELECT命令中使用了动态绑定列。
你是否知道任何用于排序数据库表中列(字段顺序)的库?
更多关于Golang动态SQL创建与导入实现指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
该方法输出的格式是数组,我们需要的是JSON格式。
有人有其他方案吗?我的客户不接受按字母顺序排列。他希望数据能根据字段中的表格进行可视化展示。
我们愿意为此开发支付费用。
嗨 @packs,
在这个场景的哪一步你遇到了问题? 你尝试了什么但没有成功? 你能分享一个导致失败的简化版代码吗?
我们从客户那里获取Excel文件。 我们不知道Excel文件中具体有多少列。(需要读取Excel列) 我们需要将Excel文件的数据存储到数据库表中(应根据Excel列动态创建列,所有列均为文本字段) 我们需要向用户展示表中的数据(需要一个动态结构来进行扫描)
func main() {
fmt.Println("hello world")
}
我搜索了“golang ordered json”,找到了这个看起来能满足你需求的包:GitHub - virtuald/go-ordered-json: 一个支持有序 JSON 解码/编码的 golang encoding/json 包的分支
话虽如此,客户为什么要在意 JSON 看起来是什么样子呢?无论 JSON 的布局如何,Web 应用程序(或任何用户界面)都应该能够以任何顺序显示数据。
result [][]string
_Columnname = []string{}
_Columnwidth = []int{}
allrows, err := tx.Queryx(appquery.Select_ccsall)
_Columnname, err = allrows.Columns()
length := len(_Columnname)
pointers := make([]interface{}, length)
container := make([]string, length)
for i, _ := range pointers {
pointers[i] = &container[i]
}
defer allrows.Close()
for allrows.Next() {
/*
pointers := make([]interface{}, length)
container := make([]string, length)
for i := range pointers {
pointers[i] = &container[i]
}
*/
allrows.Scan(pointers...)
result = append(result, container)
}
func queryToJson(db *sql.DB, query string, args …interface{}) ([]byte, error) {
// an array of JSON objects
// the map key is the field name
var objects []map[string]interface{}
rows, err := db.Query(query, args...)
if err != nil {
return nil, err
}
for rows.Next() {
// figure out what columns were returned
// the column names will be the JSON object field keys
columns, err := rows.ColumnTypes()
if err != nil {
return nil, err
}
// Scan needs an array of pointers to the values it is setting
// This creates the object and sets the values correctly
values := make([]interface{}, len(columns))
object := map[string]interface{}{}
for i, column := range columns {
object[column.Name()] = reflect.New(column.ScanType()).Interface()
values[i] = object[column.Name()]
}
err = rows.Scan(values...)
if err != nil {
return nil, err
}
objects = append(objects, object)
}
// indent because I want to read the output
return json.MarshalIndent(objects, "", "\t")
}
这段代码对我来说是有效的,我希望列的顺序与数据库表中的顺序保持一致。
packs: 这个函数不清楚。 我如何获取字段名?
你可以:
- 使用你数据库的
INFORMATION_SCHEMA(或者你的特定 RDBMS 用来描述其模式的任何其他方式)。 - 或者你可以自己存储这些信息(你可以创建一个表来跟踪动态表的列名)。
- 或者你可以使用
(*sql.Rows).Columns函数来获取列名。
packs: 这个方法给出的输出是数组格式。我们需要 JSON 格式。
我猜你的意思是希望输出看起来像 {"columnName0": "columnValue0", "columnName1": "columnValue1"}。如果是这样,你可以在返回之前,将 _Columnname 和 container 的内容合并到一个 map 中:
m := make(map[string]string, len(_Columnname))
for i, k := range _Columnname {
m[k] = container[i]
}
return m // 如果你将这个传递给 json.Marshal,你会得到一个 JSON 对象。
packs: 这段代码对我有用,我希望列的顺序与数据库表中的顺序一致。
这很难!在 Go 中,map 是无序的,并且根据 JSON 规范,JSON 对象也是无序的,所以你必须找一些第三方的 JSON 库来处理它。也许这里有人会有建议。
你好,Prakash,
packs: 我们不知道Excel文件中有多少列。(读取Excel列)
我建议你研究一下Excelize包来处理Excel文件:excelize package - github.com/xuri/excelize/v2 - Go Packages。
packs: 我们必须将Excel文件数据存储到表中(需要根据Excel列动态创建列,所有列都是文本字段)
你应该能够相当容易地生成 CREATE TABLE 命令(我还没有测试过这个,但它应该是一个很好的起点来生成用于创建表的DDL。这是为Microsoft SQL Server编写的,但应该很容易调整以适用于其他RDBMS):
type textColumn struct {
name string
length int
}
func createCreateTableSQLForTextColumnTable(tableName string, textColumns []textColumn) string {
sb := strings.Builder{}
sb.WriteString("CREATE TABLE ")
sb.WriteString(tableName)
sb.WriteString(" (\n")
for i, textCol := range textColumns {
sb.WriteByte('\t')
sb.WriteString(textcol.name)
sb.WriteString(" VARCHAR(")
sb.WriteString(strings.Atoi(textCol.length))
sb.WriteString(",\n")
}
sb.WriteString(");")
return sb.String()
}
packs: 我们必须向用户展示表数据(需要用于扫描的动态结构)
你为什么需要动态结构?因为所有列都是字符串,你不能直接调用 (*sql.Rows).Scan/(*sql.Row).Scan 并扫描到一个 []string 切片中吗?
func scanTextColumnTable(scanner interface{ Scan(...interface{}) error }, fields []string) error {
vs := make([]interface{}, len(fields))
for i := range fields {
vs[i] = &fields[i]
}
return scanner.Scan(vs...)
}
在Golang中实现动态SQL创建和导入,可以使用以下方案:
package main
import (
"database/sql"
"fmt"
"strings"
"github.com/xuri/excelize/v2"
_ "github.com/go-sql-driver/mysql"
)
type DynamicImporter struct {
db *sql.DB
}
func (di *DynamicImporter) CreateTableFromExcel(filePath, tableName string) error {
// 读取Excel文件
f, err := excelize.OpenFile(filePath)
if err != nil {
return err
}
defer f.Close()
// 获取第一个工作表
sheetName := f.GetSheetName(0)
rows, err := f.GetRows(sheetName)
if err != nil {
return err
}
if len(rows) == 0 {
return fmt.Errorf("Excel文件为空")
}
// 第一行作为列名
headers := rows[0]
// 构建CREATE TABLE语句
var columns []string
for _, header := range headers {
// 清理列名,移除特殊字符
cleanCol := strings.ReplaceAll(header, " ", "_")
cleanCol = strings.ReplaceAll(cleanCol, "-", "_")
columns = append(columns, fmt.Sprintf("`%s` TEXT", cleanCol))
}
createSQL := fmt.Sprintf(
"CREATE TABLE IF NOT EXISTS `%s` (%s)",
tableName,
strings.Join(columns, ", "),
)
// 执行创建表
_, err = di.db.Exec(createSQL)
return err
}
func (di *DynamicImporter) ImportExcelData(filePath, tableName string) error {
f, err := excelize.OpenFile(filePath)
if err != nil {
return err
}
defer f.Close()
sheetName := f.GetSheetName(0)
rows, err := f.GetRows(sheetName)
if err != nil {
return err
}
if len(rows) < 2 {
return fmt.Errorf("没有数据行")
}
headers := rows[0]
// 准备INSERT语句
placeholders := make([]string, len(headers))
for i := range headers {
placeholders[i] = "?"
}
insertSQL := fmt.Sprintf(
"INSERT INTO `%s` (%s) VALUES (%s)",
tableName,
"`" + strings.Join(headers, "`,`") + "`",
strings.Join(placeholders, ","),
)
// 准备语句
stmt, err := di.db.Prepare(insertSQL)
if err != nil {
return err
}
defer stmt.Close()
// 插入数据行
for _, row := range rows[1:] {
// 确保行数据长度与列数匹配
values := make([]interface{}, len(headers))
for i := 0; i < len(headers); i++ {
if i < len(row) {
values[i] = row[i]
} else {
values[i] = nil
}
}
_, err := stmt.Exec(values...)
if err != nil {
return err
}
}
return nil
}
// 动态SELECT示例
func (di *DynamicImporter) DynamicSelect(tableName string, columns []string, condition string, args ...interface{}) (*sql.Rows, error) {
if len(columns) == 0 {
columns = []string{"*"}
}
query := fmt.Sprintf(
"SELECT %s FROM `%s`",
strings.Join(columns, ", "),
tableName,
)
if condition != "" {
query += " WHERE " + condition
}
return di.db.Query(query, args...)
}
func main() {
// 数据库连接
db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/database")
if err != nil {
panic(err)
}
defer db.Close()
importer := &DynamicImporter{db: db}
// 创建表
err = importer.CreateTableFromExcel("data.xlsx", "dynamic_table")
if err != nil {
panic(err)
}
// 导入数据
err = importer.ImportExcelData("data.xlsx", "dynamic_table")
if err != nil {
panic(err)
}
// 动态查询示例
rows, err := importer.DynamicSelect("dynamic_table", []string{"column1", "column2"}, "column3 = ?", "value")
if err != nil {
panic(err)
}
defer rows.Close()
// 处理查询结果
for rows.Next() {
var col1, col2 string
rows.Scan(&col1, &col2)
fmt.Println(col1, col2)
}
}
需要安装的依赖:
go get github.com/xuri/excelize/v2
go get github.com/go-sql-driver/mysql
这个实现包含以下关键功能:
- 动态表创建:从Excel第一行读取列名,自动生成CREATE TABLE语句
- 数据导入:使用预处理语句批量插入数据
- 动态查询:支持动态指定查询列和条件
- 列名清理:自动处理Excel列名中的特殊字符
注意:实际使用时需要根据数据类型调整字段类型定义,并添加适当的错误处理和事务支持。


