Golang 中的动态 SQL 选择查询

Spa*_*ant 5 mysql sql go

我正在尝试使用数据库/sql 和 mysql 驱动程序构建 API,它将根据 URL 参数读取数据。
像这样的东西

myapi.com/users?columns=id,first_name,last_name,country&sort=desc&sortColumn=last_name&limit=10&offset=20
Run Code Online (Sandbox Code Playgroud)

我知道如何在结构中定义时获取所有列或仅获取特定列。但我想知道是否可以从 url 获取列,而不是将其保存到映射中,而不是预定义的结构,而不仅仅是扫描这些列。
我有工作代码,仅当列数与结构中的列数相同时,才会从上述端点获取数据。country例如,如果我删除,我会收到错误,Scan需要 4 个参数,但给出了 3 个参数。

我不需要特定的代码,只需要一些指导,因为我正在学习 Go,而且我的背景是 PHP,这更容易做到。

更新

感谢答案,我有了部分有效的解决方案。
这是代码:

cols := []string{"id", "first_name", "last_name"}
vals := make([]interface{}, len(cols))
w := map[string]interface{}{"id": 105}

var whereVal []interface{}
var whereCol []string

for k, v := range w {
    whereVal = append(whereVal, v)
    whereCol = append(whereCol, fmt.Sprintf("%s = ?", k))
}

for i := range cols {
    vals[i] = new(interface{})
}
err := db.QueryRow("SELECT "+strings.Join(cols, ",")+" FROM users WHERE "+strings.Join(whereCol, " AND "), whereVal...).Scan(vals...)

if err != nil {
    fmt.Println(err)
}

b, _ := json.Marshal(vals)
fmt.Println(string(b))
Run Code Online (Sandbox Code Playgroud)

这应该查询SELECT id, first_name, last_name FROM users WHERE id = 105;

但是如何将数据获取到正确的 json 对象呢?现在它打印出以 Base64 编码的字符串,如下所示。

[105,"Sm9obm55","QnJhdm8="]
Run Code Online (Sandbox Code Playgroud)

jak*_*kob 5

据我所知(在 Go 中也没有太多经验),如果您没有为值分配实际类型,那么Scan它将返回[]byte,并且当它被编组时,它返回 base64 编码的字符串。

因此,您必须为列分配类型,如果您想要正确的 json,则为值分配键。

在您的示例中,可以这样做:

cols := []string{"id", "first_name", "last_name"}
vals := make([]interface{}, len(cols))
result := make(map[string]interface{}, len(cols))

for i, key := range cols {
    switch key {
    case "id", "status":
        vals[i] = new(int)
    default:
        vals[i] = new(string)
    }

    result[key] = vals[i]
}

b, _ := json.Marshal(result)
fmt.Println(string(b))
Run Code Online (Sandbox Code Playgroud)

因此,现在我们不再为每列循环cols并创建新接口,而是创建键/值对并根据列名称分配类型。

另外,如果表中有可为空的列(而且可能有),那么您将收到错误,因为nil无法进入string. 所以我建议这个包gopkg.in/guregu/null.v3,然后分配像null.String. 这样你就会得到null一个值。

例如:

for i, key := range cols {
    switch key {
    case "id", "status":
        vals[i] = new(int)
    case "updated_at", "created_at":
        vals[i] = new(null.Time)
    default:
        vals[i] = new(null.String)
    }

    result[key] = vals[i]
}
Run Code Online (Sandbox Code Playgroud)