我正在尝试使用数据库/ sql和mysql驱动程序构建API,该API将根据URL参数读取数据。
像这样
myapi.com/users?columns=id,first_name,last_name,country&sort=desc&sortColumn=last_name&limit=10&offset=20
在struct中定义时,我知道如何获取所有列或仅获取特定列。但是我想知道是否有可能从url中获取列,而不是将预定义的结构保存到映射中,而不是仅扫描这些列。
我有工作代码,仅当列数与struct中相同时,才会从端点上方获取数据。例如,如果我删除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))
这应该查询SELECT id, first_name, last_name FROM users WHERE id = 105;
但是如何将数据输出到正确的json对象?现在,它会打印出以这种方式在base64中编码的字符串。
[105,"Sm9obm55","QnJhdm8="]
答案 0 :(得分:1)
您必须首先获取结果列的数量,然后不要超过其大小。
如果您要查询的字段,则需要动态创建查询字符串,参数大小必须相同。
答案 1 :(得分:1)
我将使用动态字段创建查询语句(使用占位符以避免sql注入):
rows := db.QueryRow("SELECT {{YOUR_FIELDS}} from table_tbl")
创建具有相同列大小的可变载体
vals := make([]interface{}, len(rows.Columns()))
如果不需要类型检查或不知道它们的类型,请使用sql.RawBytes作为字段的类型,否则请使用相同的字段类型。
for i, _ := range cols {
vals[i] = new(sql.RawBytes)
//check column name, if it is id, and you know it is integer
//vals[i] = new(int)
}
迭代行并扫描
for rows.Next() {
err = rows.Scan(vals...)
}
答案 2 :(得分:1)
据我所知(在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))
因此,我们不再遍历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]
}
答案 3 :(得分:0)
这里有一个发现返回动态结果集的选项,您将需要一个interface {}数组,但是您必须分配一个新的(interface {})才能获得可由Scan方法写入的指针
//...
types, _ := rows.ColumnTypes()
for rows.Next() {
row := make([]interface{}, len(types))
for i := range types {
row[i] = new(interface{})
}
rows.Scan(row...)
}