2009-04-10 6 views
5

私はこれまで何年もSQLを使用してきましたが、めったにその単純な挿入や選択などはありませんでした。 PHPからPDOまでSQLite上で実行しているより複雑なSQL文を最適化するための助けを得ることができるのだろうかと思います。SQLite最適化マルチセレクトインサート

文が正しく動作しているように見えますが、私が予想していたより長くかかりそうです(多分、あまり期待しすぎています)。

これはSQLです:

INSERT OR IGNORE INTO MailQueue(SubscriberID, TemplateID) 
    SELECT Subscribers.ID, '1' AS TemplateID 
    FROM Subscribers 
    INNER JOIN SubscriberGroups ON Subscribers.ID=SubscriberGroups.SubscriberID 
    WHERE SubscriberGroups.GroupID IN ('1', '2', '3') 
    AND Subscribers.ID NOT IN 
     ( 
     SELECT Subscribers.ID FROM Subscribers 
     INNER JOIN SubscriberGroups ON Subscribers.ID=SubscriberGroups.SubscriberID 
     WHERE SubscriberGroups.GroupID IN ('4', '5', '6') 
     ); 

私は何を持っていることは、1つ以上のグループでは、加入者のリストです。サブスクライバをメールキューに追加し、1つ以上のグループ(1,2,3)に属するものを選択しますが、別のグループ(4,5,6)のグループにも含まれているものは除外します。

まず、これを行う方法の典型的な上記のSQLがありますか?

第2に、私はこの作業をできるだけ効率的に行うためにどのような兆候が必要ですか?

現在、avg spec LAMPで約5,000の加入者レコード(および少数のグループ)を取得するのに約30秒かかります。

パフォーマンスがそれほど重大ではありませんが、この情報をよりよく理解していただければ幸いです。

ブラッド

答えて

6

余分な結合があなたを殺している可能性があります。あなたがしなければ:

SELECT Subscribers.ID, '1' AS TemplateID 
FROM Subscribers 
WHERE EXISTS(SELECT * 
       FROM SubscriberGroups 
       WHERE Subscribers.ID=SubscriberGroups.SubscriberID 
           AND SubscriberGroups.GroupID IN ('1', '2', '3')) 

    AND NOT EXISTS(SELECT * 
        FROM SubscriberGroups 
        WHERE Subscribers.ID=SubscriberGroups.SubscriberID 
        AND SubscriberGroups.GroupID IN ('4', '5', '6') 
    ); 

をまた、あなたは私の推測では、加入者はすでに、右、IDにインデックスを持っていることであるSubscriberGroups( - サブスクライバ、グループID)

にインデックスを持っていることを確認するだろうか?

EDIT: もう1つのオプション。速度が速い場合と遅くなる場合があります。

はこの1つは速くより2つの指標が求めることができ、単一のインデックススキャンかもしれ...見にそれぞれのクエリプランを見て、それは... SQLiteののオプティマイザに依存

SELECT Subscribers.ID, '1' AS TemplateID 
FROM Subscribers 
INNER JOIN(SELECT SUM(CASE WHEN GroupID IN('1', '2', '3') THEN 1 ELSE 0 END) AS inGroup, 
        SUM(CASE WHEN GroupID IN('4', '5', '6') THEN 1 ELSE 0 END) AS outGroup, 
        SubscriberID 
          FROM SubscriberGroups 
         WHERE SubscriberGroups.GroupID IN ('1', '2', '3', '4', '5', '6') 
     ) SubscriberGroups 
     ON Subscribers.ID=SubscriberGroups.SubscriberID 
     AND inGroup > 0 
     AND outGroup = 0 
+0

ありがとうマット、それは素晴らしいです。あなたの最初の解決策は30秒から約5または6になりました。これで十分です。私は本当に理解していないので、2番目のオプションを試してみませんでしたが、それが問題になる場合は、念頭に置いておきます。ありがとうございました –

3

速いかもしれないSQLを書くための別の方法(私がテストするためにSQLiteのを持っていない):

SELECT 
    S.ID, 
    '1' AS TemplateID  -- Is this really a string? Does it need to be? 
FROM 
    Subscribers S 
LEFT OUTER JOIN SubscriberGroups SG ON 
    SG.SubscriberID = S.ID 
WHERE 
    SG.SubscriberID IS NULL AND 
    EXISTS 
    (
      SELECT 
       * 
      FROM 
       SubscriberGroups SG2 
      WHERE 
       SG2.SubscriberID = S.ID AND 
       SG2.GroupID IN ('1', '2', '3') -- Again, really strings? 
    ) 

Mattの方法もうまくいくはずです。 SQLiteがどのようにクエリプランを作成するかを決めるだけです。

また、私のコメントにご注意ください。それらが実際にデータベース内のINTデータ型として定義されている場合は、2つの異なるデータ型間で変換する余分な処理があります。それらがデータベースの文字列である場合、その理由はありますか?それらの列に数値以外の値がありますか?

+0

おかげでトム、引用符で囲まれたIDの上に...私がそこにいた理由はわかりません。私はあなたの提案を試していませんでした。なぜなら、マットはうまくいくように見え、あなたのグループは除外グループの部分(4,5,6)を見逃していたようです。とにかくありがとう! –

+0

実際にはこれをより詳しく見ると、ID列の1つがINTEGERとして宣言されていないことがわかりました。これが引用符が必要な理由です。私はそれを逃した理由は、SQLiteは実際のタイプについては厄介ではありません。整数に変更して引用符を削除すると、約1/2秒で実行されます。ありがとう! –

+0

鉱山は除外事項を処理する必要があります。あなたはそれを試しましたか?それがLEFT JOIN全体についてです。 WHERE句の結合されたテーブルからNOT NULLカラムをチェックし、NULLの場合は一致するものがないことを認識します。 –