【问题标题】:How to execute a sql file如何执行一个sql文件
【发布时间】:2016-12-24 04:59:30
【问题描述】:

我有 .sql 文件,其中包含许多数据库创建、删除、填充内容。是否有可能有一个可以执行 sql 文件的 go 函数。我使用 postgres 作为我的数据库,并为所有数据库事务使用 lib/pq 驱动程序。但是我对在我的 Go 项目中执行这个 sql 文件的任何库都是开放的。

【问题讨论】:

    标签: database postgresql go


    【解决方案1】:

    如果你要使用命令行来执行它太麻烦了。您必须处理诸如设置密码、确保正确设置路径变量等问题。我认为最好的方法是使用数据库驱动程序并使用 Go 调用它。

    在以下示例中,我将 pgx implementation of sql driver 用于 Postgres。您可以使用您选择的任何驱动程序实现来做到这一点。

    path := filepath.Join("path", "to", "script.sql")
    
    c, ioErr := ioutil.ReadFile(path)
    if ioErr != nil {
       // handle error.
    }
    sql := string(c)
    _, err := *pgx.Conn.Exec(sql)
    if err != nil {
      // handle error.
    }
    

    解释:

    1. 以与操作系统无关的方式获取 sql 脚本的路径。
    2. 将文件内容读入字符串。
    3. 使用 sql 驱动程序执行文件中的语句。

    【讨论】:

    • 必须在你的连接字符串中添加?multiStatements=true,否则你会得到You have an error in your SQL syntax
    【解决方案2】:

    我在寻找类似需求时找到了 dotsql。您可以从特定文件加载命名的 sql 语句/准备语句并执行。

    // Get a database handle
    db, err := sql.Open("sqlite3", ":memory:")
    
    // Loads queries from file
    dot, err := dotsql.LoadFromFile("queries.sql")
    
    // Run queries
    res, err := dot.Exec(db, "create-users-table")
    res, err := dot.Exec(db, "create-user", "User Name", "main@example.com")
    rows, err := dot.Query(db, "find-users-by-email", "main@example.com")
    row, err := dot.QueryRow(db, "find-one-user-by-email", "user@example.com")
    
    stmt, err := dot.Prepare(db, "drop-users-table")
    result, err := stmt.Exec()
    

    见:https://github.com/gchaincl/dotsql

    【讨论】:

      【解决方案3】:

      您可以使用标准库的os/exec 包。不需要数据库驱动程序。对于 postgreSQL,代码看起来像这样:

      cmd := exec.Command("psql", "-U", psqlUser, "-h", psqlHost, "-d", psqlDBName, "-a", "-f", sqlFilePath)
      
      var out, stderr bytes.Buffer
      
      cmd.Stdout = &out
      cmd.Stderr = &stderr
      
      err := cmd.Run()
      if err != nil {
          log.Fatalf("Error executing query. Command Output: %+v\n: %+v, %v", out.String(), stderr.String(), err)
      }
      

      【讨论】:

      • 先决条件是已安装psql 工具。
      【解决方案4】:

      您可以将文件拆分为单独的请求并逐个执行:

      file, err := ioutil.ReadFile("/some/path/to/file")
      
      if err != nil {
          // handle error
      }
      
      requests := strings.Split(string(file), ";")
      
      for _, request := range requests {
          result, err := db.Exec(request)
          // do whatever you need with result and error
      }
      

      【讨论】:

      • 如果你的 sql 文件中有过程,它将无法工作。
      • requests := strings.Split(string(file), ";") 在 SQL 的情况下会中断:INSERT INTO testtable(test) VALUES('abc;def');
      • 建议使用strings.Split(string(file), ";\n")来避免@GeneS提到的问题。
      • ";\n" 也适用于panic: Error 1065: Query was empty 错误
      • 如果您有使用 PLpgSQL 的触发器,这将不起作用,因为它需要解析 BEGIN 和 END 等...
      【解决方案5】:

      这里有 2 个选项:

      1. 使用sql驱动连接db并执行sql语句: 这是一个使用额外事务的完整示例:

        func loadSQLFile(db *sqlx.DB, sqlFile string) error {
          file, err := ioutil.ReadFile(sqlFile)
          if err != nil {
              return err
          }
          tx, err := db.Begin()
          if err != nil {
              return err
          }
          defer func() {
             tx.Rollback()
          }()
          for _, q := range strings.Split(string(file), ";") {
              q := strings.TrimSpace(q)
              if q == "" {
                 continue
              }
             if _, err := tx.Exec(q); err != nil {
                 return err
             }
          }
          return tx.Commit()
        }
        

      用法:

      loadSQLFile(db, "data/my_file.sql")
      
      1. 执行 bash 命令的第二个选项:

        func loadSQLFile(db, user, host, sqlFile string) error {
          cmd := exec.Command("psql", "-U", user, "-h", host, "-d", db, "-a", "-q", "-f", sqlFile)
          cmd.Stdout = os.Stdout
          cmd.Stderr = os.Stderr
          return cmd.Run()
        }
        

      用法:

      loadSQLFile("my_db", "my_user", "localhost", "data/my_file.sql")
      

      虽然第二个选项看起来更简单、更短,但我推荐第一个选项,它可以让您更好地控制自己的工作,最重要的是它更便携。

      【讨论】:

        【解决方案6】:

        这对我有用

        func loadSqlFile(db *sqlx.DB) {
            // Read file
            file, err := ioutil.ReadFile("./test/mock.sql")
            if err != nil {
                fmt.Println(err.Error())
            }
        
            // Execute all
            _, err = db.Exec(string(file))
            if err != nil {
                fmt.Println(err.Error())
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2016-12-12
          • 2021-03-13
          • 2010-10-24
          • 2021-12-13
          • 2018-09-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多