1

Railsに結合テーブルがあります。これは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つにフォームが入力されないためです。株と株価の履歴があります。株価履歴はフォームで作成されることはなく、yahooFinanceAPIを使用して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_idINを変数と交換することもできます。私のはでしたsecond_id IN #{variable}

于 2013-03-24T05:58:58.363 に答える
0

対処方法は次のとおりです。一時テーブルを作成し、新しい値を入力します。次に、古い結合値テーブルをロックして同時変更を防止し(重要)、新しいテーブルには表示されるが古いテーブルには表示されないすべての値のペアを挿入します。

これを行う1つの方法は、古い値を新しい値に左外部結合し、古い結合テーブルの値がnullである行をフィルタリングすることです。別のアプローチは、EXISTSサブクエリを使用することです。いずれにせよ、クエリオプティマイザーが実行されると、この2つは同じクエリプランになる可能性が高くなります。

例、テストされていません(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、書き込みテーブルのロックを保持したまましばらく実行する2番目のクエリを使用することで、かなり簡単に更新できます。

上記で指定したロックモードはsをブロックせず、、、、などのSELECT書き込みのみを行うため、プロセスの進行中もテーブルに対してクエリを実行し続けることができ、更新することはできません。INSERTUPDATEDELETE

代替手段が更新をSERIALIZABLE単独で実行することであることに同意できない場合(Pg 9.1以降では、この目的でのみ適切に機能します)。これにより、同時書き込みが発生するたびにクエリが失敗するため、何度も何度も再試行する準備をする必要があります。そのため、しばらくの間テーブルをロックしたままで生活する方が良いでしょう。

于 2013-03-24T04:36:33.237 に答える