猿问

Go SQL查询不一致

我在执行查询时遇到了一些非常奇怪的不一致,想知道是否有人知道为什么。


假设我有一个定义如下的结构:


type Result struct {

    Afield string      `db:"A"`

    Bfield interface{} `db:"B"`

    Cfield string      `db:"C"`

    Dfield string      `db:"D"`

}

以及具有以下列的 MySQL 表:


A : VARCHAR(50)

B : INT

C : VARCHAR(50)

D : VARCHAR(50)

我要执行的查询:


从表中选择 A、B、C、D WHERE A="a"


它可以执行的第一种方式:


db.Get(&result, `SELECT A, B, C, D FROM table WHERE A="a"`)

第二种执行方式:


db.Get(&result, `SELECT A, B, C, D FROM table WHERE A=?`, "a")

我遇到的不一致情况如下: 第一种方式执行查询时,Bfield 的类型是int. 但是,当第二次执行查询时,它是[]uint8。


例如,当 B 为 1 时,就会出现这种结果。


为什么 Bfield 的类型会根据查询的执行方式而有所不同?


连接声明:


// Connection is an interface for making queries.

type Connection interface {

    Exec(query string, args ...interface{}) (sql.Result, error)

    Get(dest interface{}, query string, args ...interface{}) error

    Select(dest interface{}, query string, args ...interface{}) error

}

编辑


使用 Go 数据库/sql 包 + 驱动程序也会发生这种情况。下面的查询分别分配Bfield给[]uint8和int64。


db 是 *sql.DB 类型


查询 1:


db.QueryRow(SELECT A, B, C, D FROM table WHERE A="a").Scan(&result.Afield, &result.Bfield, &result.Cfield, &result.Dfield)


--> 类型Bfield是[]uint8


查询 2:


db.QueryRow(SELECT A, B, C, D FROM table WHERE A=?, "a").Scan(&result.Afield, &result.Bfield, &result.Cfield, &result.Dfield)


--> 类型Bfield是int64


编辑


还有一点需要注意,当链接多个 WHERE 子句时,只要使用 填充至少?1 个,查询就会返回int。否则,如果它们都填充在字符串中,它将返回[]uint8


翻过高山走不出你
浏览 126回答 2
2回答

慕尼黑8549860

简短回答:因为 MySQL 驱动程序对带参数和不带参数的查询使用不同的协议。使用准备好的语句来获得一致的结果。以下解释参考标准MySQL驱动在第一种情况下,驱动程序直接将查询发送到 MySQL,并将结果解释为结构*textRows。这个结构(几乎)总是将结果解码为一个字节 slice,并将转换为更好的类型留给 Gosql包。如果目标是int,等,这可以string正常工作sql.Scanner,但不适用于interface{}.在第二种情况下,驱动程序检测到有参数并返回driver.ErrSkip。这会导致 Go SQL 包使用 PreparedStatement。在这种情况下,MySQL 驱动程序使用一个*binaryRows结构来解释结果。此结构使用声明的列类型(INT在本例中)解码值,在本例中将值解码为int64.有趣的事实:如果您将interpolateParams=true参数提供给数据库 DSN(例如"root:testing@/mysql?interpolateParams=true"),MySQL 驱动程序将在客户端准备查询,而不是使用 PreparedStatement。此时,两种类型的查询行为相同。一个小的概念证明:package mainimport (    "database/sql"    "log"    _ "github.com/go-sql-driver/mysql")type Result struct {    Afield string    Bfield interface{}}func main() {    db, err := sql.Open("mysql", "root:testing@/mysql")    if err != nil {        log.Fatal(err)    }    defer db.Close()    if _, err = db.Exec(`CREATE TABLE IF NOT EXISTS mytable(A VARCHAR(50), B INT);`); err != nil {        log.Fatal(err)    }    if _, err = db.Exec(`DELETE FROM mytable`); err != nil {        log.Fatal(err)    }    if _, err = db.Exec(`INSERT INTO mytable(A, B) VALUES ('a', 3)`); err != nil {        log.Fatal(err)    }    var (        usingLiteral         Result        usingParam           Result        usingLiteralPrepared Result    )    row := db.QueryRow(`SELECT B FROM mytable WHERE A='a'`)    if err := row.Scan(&usingLiteral.Bfield); err != nil {        log.Fatal(err)    }    row = db.QueryRow(`SELECT B FROM mytable WHERE A=?`, "a")    if err := row.Scan(&usingParam.Bfield); err != nil {        log.Fatal(err)    }    stmt, err := db.Prepare(`SELECT B FROM mytable WHERE A='a'`)    if err != nil {        log.Fatal(err)    }    defer stmt.Close()    row = stmt.QueryRow()    if err := row.Scan(&usingLiteralPrepared.Bfield); err != nil {        log.Fatal(err)    }    log.Printf("Type when using literal:  %T", usingLiteral.Bfield)         // []uint8    log.Printf("Type when using param:    %T", usingParam.Bfield)           // int64    log.Printf("Type when using prepared: %T", usingLiteralPrepared.Bfield) // int64}

浮云间

您在 MySql 中的第一个 SQL 字符串是不明确的,根据 SQL-MODE,您的 SQL 命令可以解释为SELECT A, B, C, D FROM table WHERE A='a'这就是我认为你所期待的。或者作为SELECT A, B, C, D FROM table WHERE A=`a`为了避免这种歧义,你能做一个新的 FIRST 测试来用单引号替换双引号吗?如果相同的行为继续存在,我的回答不是一个好的回应。如果 BOTH SQL select 返回相同的值,你的问题就解决了。使用 ` 字符,您传递的是变量名而不是字符串值!
随时随地看视频慕课网APP

相关分类

Go
我要回答