英文:
Go package "database/sql" got the diff-result when use the same SQL-query
问题
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, _ := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test")
// Just Query
rows, _ := db.Query("SELECT id FROM test_1 WHERE id=123456")
for rows.Next() {
var id interface{}
rows.Scan(&id)
fmt.Println("Query no args =>", id)
}
// Query with args
rows, _ = db.Query("SELECT id FROM test_1 WHERE id=?", 123456)
for rows.Next() {
var id interface{}
rows.Scan(&id)
fmt.Println("Query has args =>", id)
}
}
输出:
$ go run main.go
Query no args => [49 50 51 52 53 54]
Query has args => 123456
问题:
在我看来,"SELECT id FROM test_1 WHERE id=123456"
和 "SELECT id FROM test_1 WHERE id=?"
是相同的 SQL 查询语句。
为什么结果的类型不同?
这是一个 bug 还是只是不友好的 API 设计?
英文:
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, _ := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test")
// Just Query
rows, _ := db.Query("SELECT id FROM test_1 WHERE id=123456")
for rows.Next() {
var id interface{}
rows.Scan(&id)
fmt.Println("Query no args =>", id)
}
// Query with args
rows, _ = db.Query("SELECT id FROM test_1 WHERE id=?", 123456)
for rows.Next() {
var id interface{}
rows.Scan(&id)
fmt.Println("Query has args =>", id)
}
}
Output:
$ go run main.go
Query no args => [49 50 51 52 53 54]
Query has args => 123456
Question:
In my mind, "SELECT id FROM test_1 WHERE id=123456"
and "SELECT id FROM test_1 WHERE id=?", 123456
are same SQL-query.
Why the result's TYPE is not same?
Is it a bug or just unfriendly API design?
答案1
得分: 1
这与问题#366有关。MySQL中有两种协议:
- 文本协议。使用文本协议时,无论列的类型如何,查询结果都将以
[]byte
的形式存储。相关的源代码可以在textRows.readRow
方法中找到。进一步的转换将在rows.Scan
中进行,这是database/sql
包的一部分。在这里,结果将从[]byte
转换(如果可转换)为传递给Scan
的参数类型。 - 较新的二进制协议。使用此协议时,查询结果将根据列的类型转换为适当的类型。相关的源代码可以在
binaryRows.readRow
中找到。
在这个问题中,由于Scan
参数的类型是interface{}
,在database/sql
方面,不会进行任何转换。当发出一个没有参数的查询
时,驱动程序似乎会使用文本协议,但是当查询有参数时,驱动程序将创建预处理语句,然后使用二进制协议与服务器通信。简而言之:
- 没有参数的查询:文本协议 → 结果为
[]byte
→ 结果被扫描为interface{}
→ 结果作为123456
的ASCII码返回(即[49 50 51 52 53 54]
)。 - 带有参数的查询:二进制协议 → 根据列类型(
int64
)进行转换 → 结果被扫描为interface{}
→ 结果作为int64
返回。
如果你想获得相同的结果,可以使用以下代码进行第一个查询(为了清晰起见,省略了错误处理):
//注释掉
//rows, _ := db.Query("SELECT id FROM test_1 WHERE id=123456")
//然后替换为
stmt, _ := db.Prepare("SELECT id FROM test_1 WHERE id=123456")
defer stmt.Close()
rows, _ := stmt.Query()
英文:
This is related to issue #366. There are two protocols in MySQL:
- Text protocol. When text protocol is used, regardless of column's type, the query result will be stored as
[]byte
. Related source code can be found at methodtextRows.readRow
. Further conversion will be done duringrows.Scan
which is part ofdatabase/sql
package. Here, the result will be converted (if convertible) from[]byte
to the type of argument being passed toScan
. - Newer binary protocol. When this protocol is used, the query result will be converted to appropriate type based on column's type. Related source code can be found at
binaryRows.readRow
.
In this question, since the type of Scan
argument is interface{}
, in database/sql
side, no conversion will occurs. When issuing a query without args
, it seems that the driver will used text protocol, but when the query has argument(s), the driver will create prepared-statement then communicate to the server using binary protocol. In short:
- Query no args: text protocol → result in
[]byte
→ result scanned tointerface{}
→ result is returned as ASCII code of123456
(i.e.[49 50 51 52 53 54]
). - Query with args: binary protocol → conversion based on column type (
int64
) → result scanned tointerface{}
→ result returned asint64
.
If you want to get same result, use the following code for first query (for clarity, error handling is omitted):
//Comment out
//rows, _ := db.Query("SELECT id FROM test_1 WHERE id=123456")
//then replace with
stmt, _ := db.Prepare("SELECT id FROM test_1 WHERE id=123456")
defer stmt.Close()
rows, _ := stmt.Query()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论