Golang动态SQL创建与导入实现指南

Golang动态SQL创建与导入实现指南 我们从客户那里收到Excel文件。 我们不知道Excel文件的列名。

我们需要根据Excel列名在运行时创建表。 将Excel导入到MySQL中。

我们在SELECT命令中使用了动态绑定列。


12 回复

你是否知道任何用于排序数据库表中列(字段顺序)的库?

更多关于Golang动态SQL创建与导入实现指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


该方法输出的格式是数组,我们需要的是JSON格式。

有人有其他方案吗?我的客户不接受按字母顺序排列。他希望数据能根据字段中的表格进行可视化展示。

我们愿意为此开发支付费用。

@packs

在这个场景的哪一步你遇到了问题? 你尝试了什么但没有成功? 你能分享一个导致失败的简化版代码吗?

我们从客户那里获取Excel文件。 我们不知道Excel文件中具体有多少列。(需要读取Excel列) 我们需要将Excel文件的数据存储到数据库表中(应根据Excel列动态创建列,所有列均为文本字段) 我们需要向用户展示表中的数据(需要一个动态结构来进行扫描)

func main() {
    fmt.Println("hello world")
}
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 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: 这个函数不清楚。 我如何获取字段名?

你可以:

  1. 使用你数据库的 INFORMATION_SCHEMA(或者你的特定 RDBMS 用来描述其模式的任何其他方式)。
  2. 或者你可以自己存储这些信息(你可以创建一个表来跟踪动态表的列名)。
  3. 或者你可以使用 (*sql.Rows).Columns 函数来获取列名。

packs: 这个方法给出的输出是数组格式。我们需要 JSON 格式。

我猜你的意思是希望输出看起来像 {"columnName0": "columnValue0", "columnName1": "columnValue1"}。如果是这样,你可以在返回之前,将 _Columnnamecontainer 的内容合并到一个 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

这个实现包含以下关键功能:

  1. 动态表创建:从Excel第一行读取列名,自动生成CREATE TABLE语句
  2. 数据导入:使用预处理语句批量插入数据
  3. 动态查询:支持动态指定查询列和条件
  4. 列名清理:自动处理Excel列名中的特殊字符

注意:实际使用时需要根据数据类型调整字段类型定义,并添加适当的错误处理和事务支持。

回到顶部