2017-06-21 17 views
1

私はMySQLを使用しており、クエリがあります。サブクエリもあります。MySQLとサブクエリをより効率的に作成する

SELECT * FROM rg, list, status 
WHERE (
    (rg.required_status_id IS NULL AND rg.incorrect_status_id IS NULL) || 
    (status.season_id = rg.required_status_id AND status.user_id = list.user_id) || 

    (rg.incorrect_status_id IS NOT NULL AND 
    list.user_id NOT IN (SELECT user_id FROM status WHERE user_id = list.user_id AND season_id = rg.incorrect_status_id) 
) 
) 

問題は、次のコードの一部です:

(rg.incorrect_status_id IS NOT NULL AND 
    list.user_id NOT IN (SELECT user_id FROM status WHERE user_id = list.user_id AND season_id = rg.incorrect_status_id) 
) 

表「ステータスが」user_idlist.user_idと同じであるとseason_idrg.incorrect_status_idと同じである行を持っている場合、私がチェックできる方法は?

更新

は、ここに私の現在のコードですが、それはまったく動作しません。私は何をすべきかわかりません。

SELECT * FROM rg, list, status 
    LEFT JOIN status AS stat 
    INNER JOIN rg AS rglist 
    ON rglist.incorrect_status_id = stat.season_id 
    ON stat.season_id = rglist.incorrect_status_id 

    WHERE (
    (rg.required_status_id IS NULL AND rg.incorrect_status_id IS NULL) || 
    (status.season_id = rg.required_status_id AND status.user_id = list.user_id) || 

    (rg.incorrect_status_id IS NOT NULL AND stat.user_id IS NULL) 
) 
) 

アップデート2

私は名前を変更しますが、基本的な考え方は同じです。

FROM sarjojen_rglistat, sarjojen_rglistojen_osakilpailut, kilpailukausien_kilpailut, sarjojen_osakilpailuiden_rgpisteet 
, sarjojen_kilpailukaudet, sarjojen_kilpailukausien_kilpailusysteemit 
/* , kayttajien_ilmoittautumiset */ 
/* , sarjojen_kilpailukausien_pelaajastatukset */ 

LEFT OUTER JOIN sarjojen_kilpailukausien_pelaajastatukset 
ON sarjojen_kilpailukausien_pelaajastatukset.sarjan_kilpailukausi_id = sarjojen_rglistat.vaadittu_pelaajastatus_id 

LEFT OUTER JOIN kayttajien_ilmoittautumiset 
ON kayttajien_ilmoittautumiset.kayttaja_id = sarjojen_kilpailukausien_pelaajastatukset.kayttaja_id 

さて、これは言う:見つからない

コラム:中1054不明な列 'sarjojen_rglistat.vaadittu_pelaajastatus_idを' '句に'

だから、なぜでしょうか?

「sarjojen_rglistat」という表があり、「vaadittu_pelaajastatus_id」という列があります。

+0

MySQLは 'OR'を非常にうまく最適化しません。クエリを条件ごとに別々のクエリに分割し、それらを 'UNION'と組み合わせてみてください。 – Barmar

+0

最初のケースでは、すべての 'list'と' status'行の完全な積で 'NULL'の行が結合されることを理解していますか? – Barmar

+0

他のクエリと一致しない行を検索する3番目のクエリを書くより良い方法については、http://stackoverflow.com/questions/21633115/return-row-only-if-value-doesnt-exist?lq=1を参照してください。表。 – Barmar

答えて

1

1)クエリエンジンが解釈して効率的な計画を作成するのが簡単です。

クエリの次の部分に注意を払うと、少し "奇妙な"ことが起こっているかもしれません。これはアプローチが多少複雑すぎるという手がかりです。

...(
list.user_id NOT IN (
    SELECT user_id 
    FROM status 
      /* Note the sub-query cannot ever return a user_id different 
       to the one checked with "NOT IN" above */ 
    WHERE user_id = list.user_id 
     AND season_id = rg.incorrect_status_id) 
) 

list.user_idがuser_idの他のlist.user_id以上を含むことはできません結果セットにないクエリフィルタリング。もちろん、サブクエリはゼロの結果を返す可能性があります。だから、基本的に単純な存在チェックに変わります。

ので、スタートのために、あなたはむしろ書く必要があります。

...(
NOT EXISTS (
    SELECT * 
    FROM status 
    WHERE user_id = list.user_id 
     AND season_id = rg.incorrect_status_id) 
) 

2)あなたの「一緒にテーブルを結合するもの」(これは)だけでなく、バ​​ック1に言及について明確う。

FROM rg, list, status 

これはすべての可能な行の一致の順列組み合わせである結果セットを生成参加クロスをもたらす:

あなたのクエリは、任意の結合条件を指定せずに3つのテーブルから選択します。 WHERE句が単純な場合、クエリエンジンは特定のフィルタ条件を暗黙的に結合条件に昇格できますが、そうではありません。したがって、たとえば、あなたは、各テーブルの行の非常に少数の持っている場合でも:それは途方もなく助け、それが明確に各テーブルの行が一致することを意図しているかの条件に参加して作るために

 
status 20 
rg  100 
list  1000 

Your intermediate result set (before WHERE is applied), 
would need 1000 * 100 * 20 = 2000000 rows! 

を。クエリーを読みやすく理解しやすくするだけでなく、パフォーマンス条件の悩みとなる結合条件を見落とさないようにします。

結合条件を指定するときに、一部の行が一致しない可能性があります。これは、さまざまな種類の結合の理解と理解が非常に重要であることに注意してください。特にあなたの場合、WHERE句の複雑さの大部分は、行が一致するかどうかを調べることから来ているようです。役に立つ情報については、this answerを参照してください。

FROM/WHERE句は、おそらく次のようになります。 (特定することは困難で、あなたのテーブルの関係を述べたり、クエリの入力/出力期待していた。しかし、それは正しい軌道に乗ってあなたを設定する必要がありますされていないため。)

FROM rg 
     /* Assumes rg rows form the base of the query, and not to have 
      some rg rows excluded due to non-matches in list or status. */ 
     LEFT OUTER JOIN status ON 
      status.season_id = rg.required_status_id 
     LEFT OUTER JOIN list ON 
      status.user_id = list.user_id 
WHERE rg.incorrect_status_id IS NULL 
    /* As Barmar commented, it may also be useful to break this 
     OR condition out as a separate query UNION to the above. */ 
    OR (
      rg.incorrect_status_id IS NOT NULL 
     AND NOT EXISTS (
      SELECT * 
      FROM status 
      WHERE user_id = list.user_id 
       AND season_id = rg.incorrect_status_id) 
     ) 

(注)このクエリはについて非常に明確であること表の結合方法と、結合された結果セットのフィルタリングに使用されるものとの区別。

3)最後に、そして非常に重要なことに、最良のクエリでさえ、正しいインデックスがなければほとんど効果がありません! インデックスが悪い(または逆にインデックスが良い)クエリがうまくいかない場合は、いずれかの方法で効率が悪くなります。コンピュータの速度は十分に速いため、小さなデータベースでは気付かないかもしれませんが、候補インデックスを試して、データと作業負荷に最適な組み合わせを見つけることができます。

上記のクエリでは、次のようなインデックスが必要になる可能性があります。 (いくつかはすでに主キー制約によって覆われていてもよい。)

status.season_id 
status.user_id 
list.user_id 
rg.required_status_id 
rg.incorrect_status_id 
+0

不明な列エラー? 'rg.required_status_id' – xms

+0

@xmsそれはあなたのテーブルです。あなたに' required_status_id'カラムがあるかどうかを知るべきです。確かにあなたの質問であなた自身の質問で十分に頻繁にそれを使用しています。テーブルにカラムがある場合は、間違って貼り付けた別のテーブルが 'rg'としてエイリアスされていることしか想像できません。 –

+0

私のオープニングメッセージを見てください。更新2があります。 – xms

1

あなたはORと組み合わせている3例を扱うサブクエリのUNIONを使用してください。各サブクエリで明示的なJOINを使用して、テーブルがお互いにどのように関連しているかを確認することができます(rg.required_status_id IS NULL AND rg.incorrect_status_id IS NULLのように完全なクロス積を実行しているときにはまったく関係しません)。

SELECT rg.*, list.*, status.* 
FROM rg 
CROSS JOIN list 
CROSS JOIN status 
WHERE rg.required_status_id IS NULL AND rg.incorrect_status_id IS NULL 

UNION ALL 

SELECT rg.*, list.*, status.* 
FROM rg 
JOIN status ON rg.required_status_id = status.season_id 
JOIN list ON status.user_id = list.user_id 

UNION ALL 

SELECT rg.*, list.*, status.* 
FROM rg 
CROSS JOIN list 
LEFT JOIN status ON status.user_id = list.user_id AND status.season_id = rg.required_status_id 
WHERE rg.incorrect_status_id IS NOT NULL AND status.season_id IS NULL 
+0

+1ユニオンクエリが相互に排他的である場合、 'UNION ALL'は、存在しない複製を非効率的に削除しようとすることを防ぎます。 –

+0

良い点。私は彼らがいたのかどうかはわかりませんでしたが、私はヌルフィールドの条件のために考えています。 – Barmar

+0

@Barmarあなたのコードをありがとう! 1) 'status.season_id = rg.required_status_id'または2)' status.season_id = rg.incorrect_status_id'が存在しない場合の2つのケースで行を返しますか? – xms

関連する問題