从Rails 4应用程序调用大量SQL

ran*_*its 12 ruby sql ruby-on-rails sidekiq ruby-on-rails-4

我有一个Rails 4应用程序,我与它一起使用sidekiq来运行异步作业.我通常在我的Rails应用程序之外运行的一个作业是一大堆复杂的SQL查询,它们无法真正由ActiveRecord建模.这组SQL查询与我的Rails应用程序的连接是,它应该在我的一个控制器操作被调用时执行.

理想情况下,我会从控制器中的Rails应用程序中为Sidekiq排队,继续运行查询.现在它们存储在一个外部文件中,我不完全确定Rails运行所述SQL的最佳方法是什么.

任何解决方案都很受欢

Jan*_*rik 12

我同意Sharagoz,如果您只需要运行特定查询,最好的方法是将查询字符串直接发送到连接中,例如:

ActiveRecord::Base.connection.execute(File.read("myquery.sql"))
Run Code Online (Sandbox Code Playgroud)

如果查询不是静态的,你必须编写它,我会使用Arel,它已经存在于Rails 4.x中:

https://github.com/rails/arel
Run Code Online (Sandbox Code Playgroud)

  • `ActiveRecord :: Base.connection.execute`一次不会运行多个SQL语句 (4认同)

inf*_*sed 10

你没有说你正在使用什么数据库,所以我将假设MySQL.

您可以使用mysql二进制文件来完成工作:

result = `mysql -u #{user} --password #{password} #{database} < #{huge_sql_filename}`
Run Code Online (Sandbox Code Playgroud)

或者使用ActiveRecord::Base.connection.execute(File.read("huge.sql")),但如果SQL文件中有多个SQL语句,它将无法开箱即用.

为了运行多个语句,您需要创建一个初始化程序,ActiveRecord::Base.mysql2_connection用于修补程序以允许设置MySQL的CLIENT_MULTI_STATEMENTS和CLIENT_MULTI_RESULTS标志.

创建一个新的初始化程序 config/initializers/mysql2.rb

module ActiveRecord
  class Base
    # Overriding ActiveRecord::Base.mysql2_connection
    # method to allow passing options from database.yml
    #
    # Example of database.yml
    #
    #   login: &login
    #     socket: /tmp/mysql.sock
    #     adapter: mysql2
    #     host: localhost
    #     encoding: utf8
    #     flags: 131072
    #
    # @param [Hash] config hash that you define in your
    #   database.yml
    # @return [Mysql2Adapter] new MySQL adapter object
    #
    def self.mysql2_connection(config)
      config[:username] = 'root' if config[:username].nil?

      if Mysql2::Client.const_defined? :FOUND_ROWS
        config[:flags] = config[:flags] ? config[:flags] | Mysql2::Client::FOUND_ROWS : Mysql2::Client::FOUND_ROWS
      end

      client = Mysql2::Client.new(config.symbolize_keys)
      options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
      ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

然后更新config/database.yml以添加标志:

development:
  adapter: mysql2
  database: app_development
  username: user
  password: password
  flags: <%= 65536 | 131072 %>
Run Code Online (Sandbox Code Playgroud)

我刚刚在Rails 4.1上对它进行了测试,效果很好.

资料来源:http://www.spectator.in/2011/03/12/rails2-mysql2-and-stored-procedures/


kro*_*onn 8

正如其他人所概述的那样,执行一个查询非常简单

ActiveRecord::Base.connection.execute("SELECT COUNT(*) FROM users")
Run Code Online (Sandbox Code Playgroud)

您正在谈论多个查询的20.000行sql脚本.假设您在某种程度上控制了该文件,您可以从中提取单个查询.

script = Rails.root.join("lib").join("script.sql").read # ah, Pathnames

# this needs to match the delimiter of your queries
STATEMENT_SEPARATOR = ";\n\n"


ActiveRecord::Base.transaction do
  script.split(STATEMENT_SEPARATOR).each do |stmt|
    ActiveRecord::Base.connection.execute(stmt)
  end
end
Run Code Online (Sandbox Code Playgroud)

如果你很幸运,那么查询分隔符可能是"; \n \n",但这当然取决于你的脚本.我们在另一个例子中有"\ x0"作为分隔符.关键是您将脚本拆分为查询以将它们发送到数据库.我将它包装在一个事务中,让数据库知道有多个语句.在发送脚本查询时没有引发异常时,块提交.

如果您没有控制脚本文件,请开始与控制它的人交谈,以获得可靠的分隔符.如果它不在你的控制范围内而且你无法与控制它的那个人交谈,我猜你也不会执行它:-).

UPDATE

这是解决此问题的通用方法.对于PostgreSQL,您不需要手动拆分语句.你可以一次性发送它们execute.对于MySQL,似乎有解决方案将适配器转换为CLIENT_MULTI_STATEMENTS模式.


Sha*_*goz 5

如果要通过活动记录执行原始SQL,可以使用此API: ActiveRecord::Base.connection.execute("SELECT COUNT(*) FROM users")

  • `ActiveRecord :: Base.connection.execute`一次不会运行多个SQL语句. (2认同)
  • `execute`支持PostgreSQL的多个语句.对于MySQL,有一种方法可以实现它(如@infused显示). (2认同)