我想做upsert。 Rails还没有支持这个。查询是这样的:
INSERT INTO foos (thing_id, bar_id) VALUES (1, 2)
ON CONFLICT (thing_id, bar_id) DO NOTHING
我可以使用self.class.connection.execute
或exec_insert
轻松完成此操作。但我也希望利用准备好的陈述。我想我可以这样做:
thing_id = ActiveRecord::Relation::QueryAttribute.new("thing_id", thing.id, ActiveRecord::Type::Integer.new)
bar_id = ActiveRecord::Relation::QueryAttribute.new("bar_id", id, ActiveRecord::Type::Integer.new)
self.class.connection.exec_insert(<<-SQL, nil, [thing_id, bar_id])
INSERT INTO foos (thing_id, bar_id) VALUES ($1, $2)
ON CONFLICT (thing_id, bar_id) DO NOTHING
SQL
但是当我试验这个时,似乎没有创建预备语句。
我试过这种风格:
query = <<-SQL
INSERT INTO foos (thing_id, bar_id) VALUES ($1, $2)
ON CONFLICT (thing_id, bar_id) DO NOTHING
SQL
connection = ActiveRecord::Base.connection.raw_connection
connection.prepare('some_name', query)
st = connection.exec_prepared('some_name', [ thing.id, id ])
它确实创建了一个准备好的声明。但是,第二次运行时,postgres抱怨创建一个具有相同名称的预准备语句。所以,rails&#39;准备好的报表管理发生在高于此水平的水平上,我无法在此处利用它。我必须在这里手动管理它。我不能确信我能做到这一点,即使我能做到这一点也会很冗长。
execute
和朋友不接受("foo=?", 1)
接受的where
api。
有没有办法利用rails&#39;原始SQL的自动化预处理语句管理?
答案 0 :(得分:3)
#exec_query
接受一个可选的关键字参数prepare
,默认为false。看看method definition(和here)。
给出以下表格定义:
CREATE TABLE foos (
thing_id INT,
bar_id INT,
UNIQUE (thing_id, bar_id)
);
我测试了以下内容:
conn = ActiveRecord::Base.connection
stmt = 'INSERT INTO foos (thing_id, bar_id) VALUES ($1, $2) ' \
'ON CONFLICT (thing_id, bar_id) DO NOTHING'
# "Old" bind parameters
binds = [[nil, 1], [nil, 2]]
conn.exec_query stmt, 'SQL', binds, prepare: true
conn.exec_query stmt, 'SQL', binds, prepare: true
# "New" bind parameters
type = ActiveModel::Type::Integer.new limit: 4
binds = [
ActiveRecord::Relation::QueryAttribute.new('thing_id', 1, type),
ActiveRecord::Relation::QueryAttribute.new('bar_id', 2, type)
]
conn.exec_query stmt, 'SQL', binds, prepare: true
conn.exec_query stmt, 'SQL', binds, prepare: true
这两种绑定参数样式都适用于ActiveRecord 5.2.0和pg 1.0.0。多个使用相同值的INSERT语句最终将仅插入一行,而不会引发任何错误。我检查了Postgres日志,并且只有一个“解析”(在第一个INSERT之前),因此看来准备好的语句机制正在正确使用。