英文:
Golang ORDER BY issue with MySql
问题
我无法使用db.Select()动态地进行ORDER BY。我已经尝试过谷歌搜索,但没有找到解决方法...
可行的方法
rows, err := db.Query("SELECT * FROM Apps ORDER BY title DESC")
不可行的方法
rows, err := db.Query("SELECT * FROM Apps ORDER BY ? DESC", "title")
我没有收到任何错误消息,查询只是未能排序。
英文:
I can't seem to dynamically ORDER BY with db.Select(). I've Googled without any luck...
WORKS
rows, err := db.Query("SELECT * FROM Apps ORDER BY title DESC")
DOES NOT WORK
rows, err := db.Query("SELECT * FROM Apps ORDER BY ? DESC", "title")
I'm not getting any errors, the query simply fails to order.
答案1
得分: 21
占位符('?'
)只能用于插入动态的、转义的值作为过滤参数(例如在 WHERE
部分),其中应该出现数据值,而不能用于 SQL 关键字、标识符等。你不能使用它来动态指定 ORDER BY
或 GROUP BY
的值。
不过,你仍然可以这样做,例如你可以使用 fmt.Sprintf()
来组装动态查询文本,像这样:
ordCol := "title"
qtext := fmt.Sprintf("SELECT * FROM Apps ORDER BY %s DESC", ordCol)
rows, err := db.Query(qtext)
需要注意的事项:
这样做的话,你将需要手动防止 SQL 注入攻击,例如,如果列名的值来自用户,你不能接受任何值并直接插入查询中,否则用户将能够进行各种破坏性的操作。简单来说,你应该只接受英文字母、数字和下划线('_'
)的值。
虽然不能提供完整的、全面的检查器或转义函数,但你可以使用这个简单的正则表达式,它只接受英文字母、数字和 '_
:
valid := regexp.MustCompile("^[A-Za-z0-9_]+$")
if !valid.MatchString(ordCol) {
// 无效的列名,为了防止 SQL 注入,不要继续执行
}
示例(在 Go Playground 上尝试):
fmt.Println(valid.MatchString("title")) // true
fmt.Println(valid.MatchString("another_col_2")) // true
fmt.Println(valid.MatchString("it's a trap!")) // false
fmt.Println(valid.MatchString("(trap)")) // false
fmt.Println(valid.MatchString("also*trap")) // false
英文:
Placeholders ('?'
) can only be used to insert dynamic, escaped values for filter parameters (e.g. in the WHERE
part), where data values should appear, not for SQL keywords, identifiers etc. You cannot use it to dynamically specify the ORDER BY
OR GROUP BY
values.
You can still do it though, for example you can use fmt.Sprintf()
to assemble the dynamic query text like this:
ordCol := "title"
qtext := fmt.Sprintf("SELECT * FROM Apps ORDER BY %s DESC", ordCol)
rows, err := db.Query(qtext)
Things to keep in mind:
Doing so you will have to manually defend vs SQL injection, e.g. if the value of the column name comes from the user, you cannot accept any value and just insert it directly into the query else the user will be able to do all kinds of bad things. Trivially you should only accept letters of the English alphabet + digits + underscore ('_'
).
Without attempting to provide a complete, all-extensive checker or escaping function, you can use this simple regexp which only accepts English letters, digits and '_'
:
valid := regexp.MustCompile("^[A-Za-z0-9_]+$")
if !valid.MatchString(ordCol) {
// invalid column name, do not proceed in order to prevent SQL injection
}
Examples (try it on the Go Playground):
fmt.Println(valid.MatchString("title")) // true
fmt.Println(valid.MatchString("another_col_2")) // true
fmt.Println(valid.MatchString("it's a trap!")) // false
fmt.Println(valid.MatchString("(trap)")) // false
fmt.Println(valid.MatchString("also*trap")) // false
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论