我在Rails中有一个Join表,它只是一个带有id的2列表。
为了大量插入此表,我使用
ActiveRecord::Base.connection.execute("INSERT INTO myjointable (first_id,second_id) VALUES #{values})
不幸的是,当存在重复时,这会给我带来错误。我不需要更新任何值,如果存在重复,只需转到下一个insert
。
我该怎么做?
作为一个fyi,我搜索了stackoverflow,大多数答案都有点让我理解。我还检查了postgresql文件并在rails控制台中玩了但仍无济于事。我无法想出这个,所以我希望别人可以帮我告诉我我做错了什么。
我试过的最接近的陈述是:
INSERT INTO myjointable (first_id,second_id) SELECT 1,2
WHERE NOT EXISTS (
SELECT first_id FROM myjointable
WHERE first_id = 1 AND second_id IN (...))
这个陈述的部分问题是我一次只插入1个值,而我想要一个大量插入的语句。此外,语句的second_id IN (...)
部分最多可包含100个不同的值,因此我不确定它会有多慢。
请注意,大多数情况下不应该有很多重复,所以我不确定是否大量插入临时表并找到不同的值是个好主意。
编辑以添加上下文:
我需要大量插入的原因是因为我在2个模型之间存在多对多的关系,其中1个模型永远不会被表单填充。我有股票和股票价格历史。股票价格历史永远不会以某种形式创建,而是通过使用雅虎融资API从YahooFinance中提取数据来大量插入。我使用activerecord-import gem来批量插入股票价格历史(即Model.import列,值),但我不能输入jointable.import列,因为我得到了jointable is an undefined local variable
答案 0 :(得分:1)
我最终使用WITH
子句来选择我的值并给它起一个名字。然后我插入了这些值并使用WHERE NOT EXISTS
来有效地跳过我数据库中已有的任何项目。
到目前为止看起来它正在运作......
WITH withqueryname(first_id,second_id) AS (VALUES(1,2),(3,4),(5,6)...etc)
INSERT INTO jointablename (first_id,second_id)
SELECT * FROM withqueryname
WHERE NOT EXISTS(
SELECT first_id FROM jointablename WHERE
first_id = 1 AND
second_id IN (1,2,3,4,5,6..etc))
您可以将值与变量互换。我是VALUES#{values}
您还可以将second_id IN与变量互换。我是second_id IN #{variable}
。
答案 1 :(得分:0)
以下是我要解决的问题:创建临时表并使用新值填充它。然后锁定旧的连接值表以防止并发修改(重要)并插入出现在新表中而不是旧表中的所有值对。
执行此操作的一种方法是将旧值的左外连接添加到新值上,并过滤旧连接表值为空的行。另一种方法是使用EXISTS
子查询。无论如何,一旦完成查询优化器,这两者很可能会产生相同的查询计划。
示例,未经测试(因为您没有提供SQLFiddle或示例数据)但应该可以工作:
BEGIN;
CREATE TEMPORARY TABLE newjoinvalues(
first_id integer,
second_id integer,
primary key(first_id,second_id)
);
-- Now populate `newjoinvalues` with multi-valued inserts or COPY
COPY newjoinvalues(first_id, second_id) FROM stdin;
LOCK TABLE myjoinvalues IN EXCLUSIVE MODE;
INSERT INTO myjoinvalues
SELECT n.first_id, n.second_id
FROM newjoinvalues n
LEFT OUTER JOIN myjoinvalues m ON (n.first_id = m.first_id AND n.second_id = m.second_id)
WHERE m.first_id IS NULL AND m.second_id IS NULL;
COMMIT;
这不会更新现有值,但您也可以通过使用第二个查询执行UPDATE ... FROM
同时仍然保持写表锁定来轻松地执行此操作。
请注意,上面指定的锁定模式不会阻止SELECT
,只会写入INSERT
,UPDATE
和DELETE
,因此可以继续查询当流程正在进行时,您无法更新它。
如果您不能接受另一种方法是在SERIALIZABLE
隔离中运行更新(仅在Pg 9.1及更高版本中为此目的正常工作)。每当发生并发写入时,这将导致查询失败,因此您必须准备一遍又一遍地重试它。出于这个原因,只需将表锁定一段时间就可能会更好。