如何在Rails中使用一次查询执行多个语句?

16

我正在使用Ruby on Rails与ActiveRecord和PostgreSQL。

如何执行多个SQL查询?

我需要运行一个自定义的迁移脚本,例如:

Foo.connection.execute <<-SQL.split(';').map(&:strip).join
 delete from metadata where record_type = 'Foo';
 TRUNCATE table1 RESTART IDENTITY;
 TRUNCATE table2 RESTART IDENTITY;
 delete from schema_migrations where version > '20120806120823';
SQL

我不接受用户数据,所以我不担心 SQL 注入问题。

MySQL 中类似 CLIENT_MULTI_STATEMENTS 的东西或许可以用?

来自 MySQL/PHP 文档:

CLIENT_MULTI_STATEMENTS: 告诉服务器客户端可能会在一个字符串中发送多个语句(使用“;”分隔)。如果没有设置此标志,则禁用多语句执行。有关此标志的更多信息,请参见本表后面的注释。


我理解在真实的例子中,您正在从文件中读取脚本,因此您不能像示例那样仅针对每个语句循环一次execute而不进行不安全的字符串拆分。如果它直接在您的代码中,您可以将语句放入数组中并循环遍历它。 - Craig Ringer
希望修改准确地反映了您的意图。如果不是,请告诉我 - 或重新编辑。 - Craig Ringer
就像Craig Ringer所说的那样,为什么不能只是多次调用connection.execute并每次执行一个语句呢?我已经这样做了很多次(例如,在Rails应用程序中作为第一个迁移运行遗留SQL模式转储时)。 - Alistair A. Israel
不想进行多次网络调用。但目前我正在这样做。在这种情况下,网络调用非常缓慢,但对于删除和截断,我可以接受。我问这个问题主要是为了了解情况。 - deepak
3个回答

11

它应该可以直接与PostgreSQL配合使用,已经使用pg gem和rails 3.2进行了检查:

class Multitest < ActiveRecord::Migration
  def up
    execute <<-SQL
      create table x(id serial primary key);
      create table y(id serial primary key, i integer);
    SQL
  end

  def down
  end
end

顺便说一下,直接操作 schema_migrations 看起来很奇怪。


1
谢谢,我会检查迁移代码。但是为什么迁移的执行方式可以工作而 ActiveRecord 的不能?如何从 ActiveRecord 运行多个查询? - deepak
9
不过对于MySQL没有用。ActiveRecord在Postgres上可以成功执行多个操作,但在MySQL上会失败。 - Noah Gibbs

5

是的,你需要使用CLIENT_MULTI_STATEMENTS

database.yml文件中:

development:
  adapter: mysql2
  database: project_development
  flags:
    - MULTI_STATEMENTS

然后在您的代码中:

connection.execute(multistatement_query)
# Hack for mysql2 adapter to be able query again after executing multistatement_query
connection.raw_connection.store_result while connection.raw_connection.next_result

请参见https://dev59.com/MWXWa4cB1Zd3GeqPMmlC#11246837了解详情。

2
你没有仔细阅读问题。这是针对Postgres,不是MySQL。 - ZiggyTheHamster
啊,是的。我的错。 - Envek
3
在谷歌搜索通用的多语句问题后,对我这个MySQL用户来说,找到这个答案仍然很有用! :) - Nitrodist
我无法让 MULTI_STATEMENTS 标志正常工作。公平地说,我没有尝试很努力。但最终我只是将我的语句拆分并在每个语句上调用 execute。我只有几个语句,所以这样做很好,并且不需要干扰 MySQL 或 ActiveRecord。 - Joshua Pinter

5

对于mysql

queries = File.read("/where/is/myqueries.sql")
# or
queries = <<-SQL
 TRUNCATE table1 RESTART IDENTITY;
 TRUNCATE table2 RESTART IDENTITY;
 delete from schema_migrations where version > '20120806120823';
SQL

queries.split(';').map(&:strip).each do |query| 
  execute(query)
end

您可能也想看这个问题: 从Rails 4应用程序中调用大量SQL


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接