英文:
Execute SQL script in Go directly from file or string
问题
在我的应用程序安装脚本中,我正在检查数据库是否包含任何表。如果数据库为空,我想运行 DML 和 DDL SQL 脚本。
目前并不重要它是否从一个单独的 .sql 文件中读取 SQL,所以我直接将它们放入两个字符串中 - 一个用于 DDL,一个用于 DML - 然后将它们连接起来。
我的问题是,当我尝试使用 .Exec(sqlStr) 运行生成表并将数据插入其中的脚本时,我现在遇到了这个错误:
"pq: cannot insert multiple commands into a prepared statement"
当然,我可以使用一个变通方法。类似这样:
sqlStr := sqlDML + sqlDDL
sqlStmtSlice := strings.Split(sqlStr, ";")
for i:= 0; i < len(sqlStmtSlice) i++ {
// 逐个执行每个语句!
}
然而,我不确定我是否喜欢这种方法。肯定有更好的方法来从文件中加载 SQL 脚本并执行整个批处理,对吗?你知道吗?
附注:我正在使用 Go 的 PostgreSQL 驱动程序,但我不认为这会有任何区别。
编辑:
由于目前似乎没有更好的解决方案来完成这个任务,我对上面的伪代码进行了一点改进,经过测试似乎工作得很好:
tx, err := db.Begin()
sqlStr := fmt.Sprintf(sqlDML + sqlDDL)
sqlStmtSlice := strings.Split(sqlStr, ";\r")
if err != nil {
return err
}
defer func() {
_ = tx.Rollback()
}()
for _, q := range sqlStmtSlice {
_, err := tx.Exec(q)
if err != nil {
return err
}
}
err = tx.Commit()
英文:
In my install script for my application I'm checking to see if the db contains any tables. If the database is empty I have DML and DDL SQL script I'd like to run.
It's not important that it reads the SQL from a seperate .sql file, so right now I've just put it directly into two strings - one for DDL and one for DML - and concatenated those.
My problem now is that I'm now getting this error, when trying to run the script for generating tables and inserting data into them with .Exec(sqlStr):
"pq: cannot insert multiple commands into a prepared statement"
I can of course do a workaround. Something like:
sqlStr := sqlDML + sqlDDL
sqlStmtSlice := strings.Split(sqlStr, ";")
for i:= 0; i < len(sqlStmtSlice) i++ {
// Exec() each individual statement!
}
However, I'm not sure I like that method at all. Surely there has to be a much better way of just loading a SQL script from file and executing the whole batch, right? Do you know?
Ps. I'm using the PostgreSQL driver for Go, but I don't think that makes any difference.
Edit:
Since there doesn't seem to be any better solution to get this done at the time, I made a slight improvement to the above pseudo code, which is tested and seems to work just fine:
tx, err := db.Begin()
sqlStr := fmt.Sprintf(sqlDML + sqlDDL)
sqlStmtSlice := strings.Split(sqlStr, ";\r")
if err != nil {
return err
}
defer func() {
_ = tx.Rollback()
}()
for _, q := range sqlStmtSlice {
_, err := tx.Exec(q)
if err != nil {
return err
}
}
err = tx.Commit()
答案1
得分: 7
据我所知,如果不允许多个语句查询,目前没有更好的方法。这与你使用的驱动程序无关,因为这是database/sql
包的限制。关于它是否是一个好的设计的争论是另一个问题(我相信已经有很多讨论了)。
在替代方案方面,你可以使用SQL模式迁移工具或从中获取灵感。一般的约定是使用一个语义上无关的标记,比如注释,并在这些标记周围进行分割。
关于在Go语言中的示例,你可以参考以下项目:
声明:我是rambler的开发者。不过,你应该确实看看goose,它非常酷。
英文:
As far as I know, there isn't a real better method if multiple statement queries aren't allowed. It has nothing to do with the driver you're using, as this is a database/sql
package limitation. Debating for whether or not it is a good design is another question (and I'm sure there is plenty already).
On the alternatives side, you could probably use a SQL Schema Migration tool or use inspiration from them. The general convention is to use a semantically inert marker, such as a comment, and split around theses.
For examples in golang, you can see:
Disclaimer: I'm rambler's developer. That said, you should definitively have a look at goose, which is really cool.
答案2
得分: 1
我已经翻译好了,以下是翻译的内容:
在一段时间内一直在努力解决同样的问题。我需要它出于稍微不同的原因(在运行测试之前初始化数据库)。
正如Elwinar已经提到的,database/sql
包不允许这样做。我克服这个问题的方法是创建一个Python脚本(我们称之为sql_runner.py
),该脚本执行一个文件,然后运行一个go exec命令来执行Python脚本(不要忘记将其设置为可执行)。
所以运行SQL文件的Python命令是:cursor.execute(open("setting_up.sql"), "r").read())
以下是运行该文件的Go代码的一部分:
cmd := exec.Command("sql.py")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Println(cmd.Run())
英文:
Have struggled with the same problem for some time. I needed it for a slightly different reason (initialize the database before running tests).
As Elwinar already mentioned, database/sql
package does not allow this. The way I overcame this is by creating a python script (let's call it sql_runner.py
) which executes a file and then run a go exec command to execute python script (do not forget to make it executable).
So a python command to run sql file: cursor.execute(open("setting_up.sql"), "r").read())
And part's of a Go code to run this file:
cmd := exec.Command("sql.py")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Println(cmd.Run())
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论