我的go应用程序应支持多个数据库。意思是,在不同的数据库上运行相同的二进制文件,应用所使用的数据库将由配置决定。

问题是,每个数据库都有自己的准备好的语句语法。
例子:

db.Prepare("select p, f, t from mytable where p = $1")

适用于postgres,但不适用于mysql。
db.Prepare("select p, f, t from mytable where p = ?")

将适用于mysql,但不适用于postgres。

我可以通过在运行时编辑字符串或维护多个查询来解决此问题。

有没有更好的办法?

我不想使用一个外部库进行庞大的抽象,它将控制我所有的数据库访问,但是如果有一些轻量级的库可以神奇地修复语法,那么我很满意。

编辑:
总结一下我之前所说的,令我困扰的部分是,对于mysql,您将不得不使用“?”。而对于postgres,您将不得不使用$ 1,$ 2 ...

干杯

最佳答案

我找到了db.Rebind()来解决这个问题。所以:

    name := "my name"
    var p = property{}
    // language=SQL
    s := "SELECT * FROM property WHERE name=?"
    err := db.Get(&p, db.Rebind(s), name)
顶部的语言注释使IntelliJ仍然可以在UI中为我语法检查SQL语句。
我还必须为每个数据库编写单独的CREATE语句(我的应用程序同时支持mysql,postgres和sqlite)。
我还发现mysql和sqlite之间的UPDATE语句语法相同,但是postgres需要特殊处理。由于我的UPDATE语句非常一致,因此我能够编写一个函数,仅将mysql方言kludge-translate转换为postgres方言。这绝对不是通用解决方案,但可以很好地通过我的单元测试和集成测试。 YMMV。
// RebindMore takes a MySQL SQL string and convert it to Postgres if necessary.
// The db.Rebind() handles converting '?' to '$1', but does not handle SQL statement
// syntactic changes needed by Postgres.
//
// convert: "UPDATE table_name SET a = ?, b = ?, c = ? WHERE d = ?"
// to:      "UPDATE table_name SET (a, b, c) = ROW (?, ?, ?) WHERE d = ?"
func RebindMore(db *sqlx.DB, s string) string {
    if db.DriverName() != "postgres" {
        return s
    }

    if !strings.HasPrefix(strings.ToLower(s), "update") {
        return db.Rebind(s)
    }

    // Convert a MySQL update statement into a Postgres update statement.
    var idx int
    idx = strings.Index(strings.ToLower(s), "set")
    if idx < 0 {
        log.Fatal().Msg("no SET clause in RebindMore (" + s + ")")
    }

    prefix := s[:idx+3]
    s2 := s[idx+3:]

    idx = strings.Index(strings.ToLower(s2), "where")
    if idx < 0 {
        log.Fatal().Msg("no WHERE clause in RebindMore (" + s + ")")
    }

    suffix := s2[idx:]
    s3 := s2[:idx]

    s4 := strings.TrimSpace(s3)
    arr := strings.Split(s4, ",")

    var names = ""
    var values = ""

    for i := 0; i < len(arr); i++ {
        nameEqValue := arr[i]
        s5 := strings.ReplaceAll(nameEqValue, " ", "")
        nvArr := strings.Split(s5, "=")
        if names != "" {
            names += ","
        }
        names += nvArr[0]
        if values != "" {
            values += ","
        }
        values += nvArr[1]
    }

    s6 := prefix + " (" + names + ") = ROW (" + values + ") " + suffix
    return db.Rebind(s6)
}
这样称呼:
    // language=SQL
    s := RebindMore(db, "UPDATE table_name SET a = ?, b = ? WHERE c = ?")
    db.MustExec(s, value1, value2)
在某个时候,我将需要添加迁移,并期望仅为每个DB添加单独的代码来处理差异(例如CREATE)。
值得指出的最后一件事是,MySQL和Postgres处理大写字母的方式非常不同。我最终只是将每个表和列名都转换为lower_case,以避免不必要的复杂性。

关于mysql - 在Golang中如何支持多种SQL语法(MySQL vs Postgres),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46096522/

10-12 04:54