1

我在 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

4

2 回答 2

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}

于 2013-03-24T05:58:58.363 回答
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时间内仍持有写表锁。

请注意,上面指定的锁定模式不会阻塞SELECTs,只会写入INSERT,UPDATEDELETE, 因此在进程进行时可以继续对表进行查询,只是不能更新它。

如果您不能接受另一种方法是单独运行更新SERIALIZABLE(仅适用于 Pg 9.1 及更高版本中的此目的)。每当发生并发写入时,这将导致查询失败,因此您必须准备好一遍又一遍地重试。出于这个原因,将桌子锁定一段时间可能会更好。

于 2013-03-24T04:36:33.237 回答