Golang中的跨数据库准备语句绑定(如同和在哪里)

时间:2014-12-04 06:54:37

标签: sql go prepared-statement sql-like where-in

在阅读了很多教程之后,我发现有很多方法可以在Go中的预处理语句上绑定参数,其中一些

SELECT * FROM bla WHERE x = ?col1 AND y = ?col2
SELECT * FROM bla WHERE x = ? AND y = ?
SELECT * FROM bla WHERE x = :col1 AND y = :col2
SELECT * FROM bla WHERE x = $1 AND y = $2

第一个问题,绑定参数的跨数据库方式是什么? (适用于任何数据库)

第二个问题,我没有阅读过有关LIKE语句,如何正确绑定LIKE - 语句的参数的教程吗?

SELECT * FROM bla WHERE x LIKE /*WHAT?*/

第三个问题,也没有给出IN语句的示例,如何正确绑定IN语句的参数?

`SELECT * FROM bla WHERE x IN ( /*WHAT?*/ )

3 个答案:

答案 0 :(得分:7)

绑定参数的跨数据库方式是什么?

使用database / sql,没有。每个数据库都有自己的方式来表示参数占位符。 Go数据库/ sql包不为预准备语句提供任何规范化工具。准备好的语句文本只是传递给底层驱动程序,驱动程序通常只是将它们未修改地发送到数据库服务器(或嵌入式数据库的库)。

如何正确绑定LIKE语句的参数?

您可以在like语句后使用参数占位符并将其绑定为字符串。例如,您可以将预备语句编写为:

SELECT a from bla WHERE b LIKE ?

以下是一个示例(省略错误管理处理)。

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)

// > select * from bla ;
// +------+------+
// | a    | b    |
// +------+------+
// | toto | titi |
// | bobo | bibi |
// +------+------+

func main() {

    // Open connection
    db, err := sql.Open("mysql", "root:XXXXXXX@/test")
    if err != nil {
         panic(err.Error())  // proper error handling instead of panic in your app
    }
    defer db.Close()

    // Prepare statement for reading data
    stmtOut, err := db.Prepare("SELECT a FROM bla WHERE b LIKE ?")
    if err != nil {
        panic(err.Error()) // proper error handling instead of panic in your app
    }
    defer stmtOut.Close()

    var a string
    b := "bi%"    // LIKE 'bi%'
    err = stmtOut.QueryRow(b).Scan(&a)
    if err != nil {
        panic(err.Error()) // proper error handling instead of panic in your app
    }
    fmt.Printf("a = %s\n", a)
} 

请注意,%字符是绑定字符串的一部分,而不是查询文本的一部分。

如何正确绑定IN语句的参数?

我所知道的数据库都不允许直接使用IN子句绑定参数列表。这不是数据库/ sql或驱动程序的限制,但大多数数据库服务器都不支持这种情况。

您可以通过多种方式解决问题:

  • 您可以在IN子句中使用固定数量的占位符构建查询。仅绑定您提供的参数,并通过NULL值完成其他占位符。如果您的值多于您选择的固定数字,则只需执行多次查询。这不是非常优雅,但它可以有效。

  • 您可以使用不同数量的占位符构建多个查询。 IN(?)的一个查询,IN(?,?)的第二个查询,IN(?,?,?)的第三个查询等等...将这些准备好的查询保存在语句缓存中,并选择正确的查询运行时取决于输入参数的数量。请注意,它需要内存,并且通常最大预备语句数是有限的,因此当参数数量很多时不能使用它。

  • 如果输入参数的数量很大,则将它们插入临时表中,并通过与临时表的连接将查询替换为IN子句。如果您设法在一次往返中执行临时表中的插入,则会很有效。使用Go和database / sql,这是不方便的,因为无法批量查询。

这些解决方案中的每一个都有缺点。它们都不是完美的。

答案 1 :(得分:1)

我是Go的新手,但只是为了回答第一部分:

  

第一个问题,绑定参数的跨数据库方式是什么? (适用于任何数据库)

如果你使用sqlx,它是内置sql包的超集,那么你应该可以使用sqlx.DB.Rebind来实现它。

答案 2 :(得分:1)

我也遇到了同样的问题,在阅读了答案之后,开始寻找关于如何为IN语句绑定参数的其他解决方案。

这是我所做的事的一个例子,不是最优雅的解决方案,但对我有用。

我要做的是创建一个带有静态设置在查询上的参数的选择查询,而根本不使用绑定功能。

为了确保安全,最好对Marshal命令产生的字符串进行清理,但现在我不需要了。

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "log"

    _ "github.com/go-sql-driver/mysql"
)

type Result struct {
    Identifier string
    Enabled    bool
}

func main() {

    // Open connection
    db, err := sql.Open("mysql", "username:password@tcp(server-host)/my-database")
    if err != nil {
        panic(err.Error()) // proper error handling instead of panic in your app
    }
    defer db.Close()

    // this is an example of a variable list of IDs
    idList := []string{"ID1", "ID2", "ID3", "ID4", "ID5", "IDx"}

    // convert the list to a JSON string
    formatted, _ := json.Marshal(idList)

    // a JSON array starts and ends with '[]' respectivelly, so we replace them with '()'
    formatted[0] = '('
    formatted[len(formatted)-1] = ')'

    // create a static select query
    query := fmt.Sprintf("SELECT identifier, is_enabled FROM some_table WHERE identifier in %s", string(formatted))

    // prepare que query
    rows, err := db.Query(query)
    if err != nil {
        panic(err.Error()) // proper error handling instead of panic in your app
    }
    defer rows.Close()

    var result []Result
    // fetch rows
    for rows.Next() {
        var r0 Result
        if err := rows.Scan(&r0.Identifier, &r0.Enabled); err != nil {
            log.Fatal(err)
        }
        // append the row to the result
        result = append(result, r0)
    }
    if err := rows.Err(); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("result = %v\n", result)
}