如何使用 Arel 批量插入?
How to bulk insert with Arel?
我有类似下面的循环,它创建了许多插入:
table = Arel::Table.new(:users)
users.each do |user|
manager = Arel::InsertManager.new(ActiveRecord::Base)
manager.into(table).insert([
[table[:name], user.name],
[table[:created_at], user.created_at],
[table[:updated_at], user.updated_at]
])
# INSERT INTO users (name) VALUES ('a')
@conn.insert(manager.to_sql)
end
但是,我想找到一种方法来只获取要插入的值。这样我就可以对所有值进行一次插入。
PS:请注意我不想写 sql 字符串(安全问题)。
table = Arel::Table.new(:users)
inserts = []
users.each do |user|
manager = Arel::InsertManager.new(ActiveRecord::Base)
manager.into(table).insert([
[table[:name], user.name],
[table[:created_at], user.created_at],
[table[:updated_at], user.updated_at]
])
inserts << "(#{??manage values??})"
end
# INSERT INTO users (name) VALUES ('a'), ('b'), ('c')
@conn.insert(????)
本月早些时候出现了一个名为 bulk_insert
的库。这应该为您提供您正在寻找的功能:
Model.bulk_insert do |worker|
worker.add(...)
worker.add(...)
...
end
# Produces:
INSERT INTO models (...) VALUES
(...),
(...),
(...),
(...),
...
由于它是一个相当新的库,而且我还没有机会使用它,所以我不确定它是否具有 Arel 功能。
您可以使用 Active Record Import.
寻找防止 sql 注入的 bulk_insert (thanks to @Gavin Miller), I realised they use Rails cote。
因为我需要定制的东西,所以我不会选择 gem。所以,我的最终解决方案如下,以防其他人有类似的要求:
inserts = []
users.each do |user|
inserts << %{(
#{@conn.quote(user.name)},
#{@conn.quote(user.created_at)},
#{@conn.quote(user.updated_at)}
)}
end
sql = %{
INSERT INTO users
(name, created_at, updated_at)
VALUES #{inserts.join(',')}
}
@conn.execute(sql)
如果您的 AREL 版本已经支持 create_tuple
,它将像:
table = Table.new(:users)
manager = Arel::InsertManager.new
manager.into table
manager.columns << table[:id]
manager.columns << table[:name]
manager.values = manager.create_tuple([
manager.create_values(%w{ 1 david }),
manager.create_values(%w{ 2 kirs }),
manager.create_values(["3", Arel.sql('DEFAULT')], []),
])
# INSERT INTO "users" ("id", "name") VALUES ('1', 'david'), ('2', 'kirs'), ('3', DEFAULT)
end
我有类似下面的循环,它创建了许多插入:
table = Arel::Table.new(:users)
users.each do |user|
manager = Arel::InsertManager.new(ActiveRecord::Base)
manager.into(table).insert([
[table[:name], user.name],
[table[:created_at], user.created_at],
[table[:updated_at], user.updated_at]
])
# INSERT INTO users (name) VALUES ('a')
@conn.insert(manager.to_sql)
end
但是,我想找到一种方法来只获取要插入的值。这样我就可以对所有值进行一次插入。
PS:请注意我不想写 sql 字符串(安全问题)。
table = Arel::Table.new(:users)
inserts = []
users.each do |user|
manager = Arel::InsertManager.new(ActiveRecord::Base)
manager.into(table).insert([
[table[:name], user.name],
[table[:created_at], user.created_at],
[table[:updated_at], user.updated_at]
])
inserts << "(#{??manage values??})"
end
# INSERT INTO users (name) VALUES ('a'), ('b'), ('c')
@conn.insert(????)
本月早些时候出现了一个名为 bulk_insert
的库。这应该为您提供您正在寻找的功能:
Model.bulk_insert do |worker|
worker.add(...)
worker.add(...)
...
end
# Produces:
INSERT INTO models (...) VALUES
(...),
(...),
(...),
(...),
...
由于它是一个相当新的库,而且我还没有机会使用它,所以我不确定它是否具有 Arel 功能。
您可以使用 Active Record Import.
寻找防止 sql 注入的 bulk_insert (thanks to @Gavin Miller), I realised they use Rails cote。
因为我需要定制的东西,所以我不会选择 gem。所以,我的最终解决方案如下,以防其他人有类似的要求:
inserts = []
users.each do |user|
inserts << %{(
#{@conn.quote(user.name)},
#{@conn.quote(user.created_at)},
#{@conn.quote(user.updated_at)}
)}
end
sql = %{
INSERT INTO users
(name, created_at, updated_at)
VALUES #{inserts.join(',')}
}
@conn.execute(sql)
如果您的 AREL 版本已经支持 create_tuple
,它将像:
table = Table.new(:users)
manager = Arel::InsertManager.new
manager.into table
manager.columns << table[:id]
manager.columns << table[:name]
manager.values = manager.create_tuple([
manager.create_values(%w{ 1 david }),
manager.create_values(%w{ 2 kirs }),
manager.create_values(["3", Arel.sql('DEFAULT')], []),
])
# INSERT INTO "users" ("id", "name") VALUES ('1', 'david'), ('2', 'kirs'), ('3', DEFAULT)
end