使用像这样的简单模型
class Model < ActiveRecord::Base
# ...
end
我们可以做那样的查询
Model.where(["name = :name and updated_at >= :D", \
{ :D => (Date.today - 1.day).to_datetime, :name => "O'Connor" }])
其中散列中的值将被替换为最终的SQL语句,并根据底层数据库引擎进行适当的转义。
我想知道SQL执行的类似功能,如:
ActiveRecord::Base.connection.execute( \
["update models set name = :name, hired_at = :D where id = :id;"], \
{ :id => 73465, :D => DateTime.now, :name => "O'My God" }] \
) # THIS CODE IS A FANTASY. NOT WORKING.
(请不要解决加载Model对象,修改然后保存的例子!这个例子只是我希望拥有/知道的功能的一个例子。专注于这个主题!)
最初的问题是我想在数据库中插入大量(数千行)数据。我想使用ActiveRecord框架的SQL抽象的一些功能,但我不想使用基于ActiveRecord :: Base的模型对象,因为它们非常慢! (对于我当前的问题,每秒8次查询。)
答案 0 :(得分:3)
query = ActiveRecord::Base.connection.raw_connection.prepare("INSERT INTO users (name) VALUES(:name)")
query.execute(:name => 'test_name')
query.close
答案 1 :(得分:3)
使用散列插入的具体代码示例扩展@peufeu解决方案:
users_places = []
users_values = []
timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S')
params[:users].each do |user|
users_places << "(?,?,?,?)"
users_values << user[:name] << user[:punch_line] << timestamp << timestamp
end
bulk_insert_users_sql_arr = ["INSERT INTO users (name, punch_line, created_at, updated_at) VALUES #{users_places.join(", ")}"] + users_values
begin
sql = ActiveRecord::Base.send(:sanitize_sql_array, bulk_insert_users_sql_arr)
ActiveRecord::Base.connection.execute(sql)
rescue
"something went wrong with the bulk insert sql query"
end
这是reference to sanitize_sql_array method in ActiveRecord::Base,它通过转义字符串中的单引号来生成正确的查询字符串。例如,punch_line“不要让他们让你失望”将成为“不要让他们让你失望”。
答案 2 :(得分:1)
对于INSERT,使用长VALUES子句(如Simon的链接所示)对它们进行批处理是最快的方法(除非您想生成文本文件并使用MySQL的LOAD DATA INFILE将其加载到数据库中)。但是你必须非常小心地转义文本值(在示例中没有这样做)。
我问的是“你使用的是什么数据库”,因为它对于大量的UPDATE来说很重要。
例如,您可以在postgres上执行此操作(我相信SQL Server将“columnX”更改为“colX”):
UPDATE foo
JOIN (VALUES (1,2),(3,4),... long list) v ON (foo.id=v.column1)
SET foo.bar = v.column2
您可以使用单个语句更新一行行,非常快。
如果您不需要Ruby在数据上执行某些特定于Ruby的魔术,那么将数据从一个数据库传输到另一个数据库的最快方法是导出为文本文件(CSV或制表符分隔),加载它在另一个DB(MySQL上的LOAD DATA INFILE)上,可能在临时表中,并使用SQL进行批量处理。
编辑:这是我在Python中的表现:
sql = [ "INSERT INTO foo (column list) VALUES " ]
values = []
for tuple in tuple_list:
append "(?,?,?,?)" to sql
extend values list with tuple
然后将sql加入一个字符串,你得到“INSERT INTO foo(列列表)VALUES(?,?,?,?),(?,?,?,?),(?,?,?,?) “与”(?,?,?,?)“重复多次,因为你要插入行。
然后“值”包含(a1,b1,c1,d1,a2,b2,c2,d2,a3,b3,c3,d3)的列表,其中a,bn,cn,dn是你想要的元组插入行n。每个对应于sql字符串中的占位符。
然后将其传递给通常的“带参数的执行查询”功能,该功能将照常处理引用和转义。
答案 3 :(得分:1)
是的,您可以执行原始SQL,但请检查有助于批量插入的ar-extensions gem:
https://github.com/zdennis/ar-extensions
这是关于它的帖子,以及其他各种技巧:
答案 4 :(得分:1)
最近我遇到类似的问题,在使用mysql2 gem绑定到Rails 4应用程序的MySQL数据库中插入100K +记录。数据包括在插入之前必须消毒的字符。
中描述的选项3的略微修改版本以下是上述链接中的相关代码块:
TIMES = 10000
inserts = []
TIMES.times do
inserts.push "(3.0, '2009-01-23 20:21:13', 2, 1)"
end
sql = "INSERT INTO user_node_scores (`score`, `updated_at`, `node_id`, `user_id`) VALUES #{inserts.join(", ")}"
我所做的修改是对需要它的值使用公共方法ActiveRecord :: Base.sanitize()。
inserts = []
created = Time.now.strftime "%Y-%m-%d %H:%M:%S"
params[:audits].each do |audit|
inserts.push "(#{audit.user_id), #{created}," + ActiveRecord::Base.sanitize(audit.comment) + ", #{audit.status})"
end
sql = "INSERT INTO user_audits (`user_id`, `created_at`, `comment`, `status`) VALUES #{inserts.join(", ")}"