如何在Active Record中检索批量插入的已创建ID列表?

我有三个型号:

class Coupon  :destroy has_many :events, :through => :coupon_events end class Event  :destroy has_many :coupons, :through => :coupon_events end class CouponEvent < ActiveRecord::Base belongs_to :coupon belongs_to :event end 

我通读了一个CSV文件来创建优惠券和coupon_events。 这非常低效,因为记录一次创建一个并导致多个查询,每个查询包括两个插入语句。

我想使用这样的单个插入查询:

 coupon_string = " ('abc','AAA'), ('123','BBB')" Coupon.connection.insert("INSERT INTO coupons (code, name) VALUES"+coupon_string) 

然后我需要为CouponEvent模型创建第二个插入查询,但我需要一个返回的coupon_ids列表。 是否有内置方法在插入时检索ID?

如果您使用的是mysql并且没有在另一个脚本/进程中插入更多行,则可以使用last_insert_id()获取插入的第一行的id

  first_id = ActiveRecord::Base.connection.execute("select last_insert_id()").first[0] 

然后顺序生成其他记录的ID。

  data = %w(one two three) to_insert = "('" + data.join("'), ('") + "')" Model.connection.insert("INSERT INTO models(name) VALUES #{to_insert}") first_id = ActiveRecord::Base.connection.execute("select last_insert_id()").first[0].to_i hash = {} data.each_with_index {|d, i| hash[first_id + i] = d} 

目前,最好的(但不是理想的)解决方案是使用“activerecord-import”进行批量导入。 不幸的是,该gem不会返回插入的id,因此您必须转身并查询以获取ID。 也就是说,您将批量插入Events模型,查询db以将它们全部返回到内存中。 现在您有了事件ID,因此您可以创建优惠券并批量插入它们。 为CouponEvents冲洗泡沫重复。

与每次活动往返一次,Coupon和CouponEvent相比 – 对于包含数千行的文件,可能需要数千次往返 – 每个模型只进行2次往返 – 一次插入事件,一次用ids获取事件,同上优惠券和优惠券活动 – 共6次往返。

实际上我不确定这个colud是否有效(如果它创建了一个插入查询),但你可以尝试使用#create方法和参数数组:

 new_coupons = Coupon.create([ { :code => "abc", :name => "AAA" }, { :code => "123", :name => "BBB" } ]) CouponEvent.create([ { :enevt_id => ..., coupon_id: ...}, ... ]) 

要为CouponEvent创建参数列表,您需要将返回的new_coupons集合映射到id,并根据优惠券代码/名称添加event_id(取决于它是否存储在CVS文件中)。

更新

我自己检查,如果第一个解决方案不起作用(我的代码中没有简单的模型没有唯一性限制,所以我没有检查过),并且你使用PostgreSQL,你总是可以这样做:

 res = Coupon.connection.execute(<<-EOSQL) INSERT INTO coupons (code, name) VALUES #{values} RETURNING id, code EOSQL 

你需要最后一个“返回”子句,这样你就可以获取插入行的代码所插入的id。 你需要映射结果集:

 res.map {|row| { :coupon_id => row["id"], :event_id => events.find { |e| e.coupon_code == row["code"] } } } 

SQL中没有标准方法可以返回插入行的列,“RETURNING”子句仅适用于PostgreSQL,因此如果使用不同的数据库,则需要检查文档或逐行插入行。

您也不能使用connection.insert ,因为在ActiveRecord中它只返回一个插入行的id,而不是所有行。

connection.insert只返回一个id,公用表表达式适合我

  insert_sql = <<-SQL WITH inserted_ids AS ( INSERT INTO clients (email, name) VALUES #{array.join(', ')} RETURNING id ) SELECT * FROM inserted_ids SQL result = ActiveRecord::Base.connection.execute(insert_sql) 

执行此操作的方法是插入具有唯一import_id值的记录。 步骤是:

  1. import_id列添加到表中。 可以是INTVARCHAR具体取决于您如何生成随机ID。

  2. 在第一次INSERT之前,生成一个随机ID。

  3. 执行第一个多值INSERT ,每行使用相同的import_id

  4. SELECT id FROM first_table WHERE import_id=

  5. 使用返回的ID生成第二个多值INSERT