2017-10-26 5 views
2
Can anybody help me optimize this mysql query? 
The query is taking more than 1 min to fetch 1000 rows of data. 
The sub query with FIND_IN_SET(id,GROUP_CONCAT(t.id))!=0 in the select statement is taking too much time to fetch data. 

同じテーブルを複数回結合する必要があるクエリを最適化するためのヒントを教えてください。 問合せは、下記される:同じテーブルを複数回結合する際にMySQLクエリを最適化する

SELECT mem.id, 
    CASE 
    WHEN t.due_date IS NOT NULL THEN 
    (SELECT description FROM descriptionTable WHERE CODE= 
    (SELECT task_type FROM memberTask WHERE FIND_IN_SET(id,GROUP_CONCAT(t.id))!=0 AND due_date=MIN(t.due_date) LIMIT 1)) 
    ELSE 
    (SELECT description FROM descriptionTable WHERE CODE= 
    (SELECT task_type FROM memberTask WHERE FIND_IN_SET(id,GROUP_CONCAT(t1.id))!=0 AND due_date=MIN(t1.due_date) LIMIT 1)) 
    END AS task_type 
    FROM member mem 
    INNER JOIN memberProgram p ON mem.id=p.member_id 
    LEFT JOIN memberTask t ON t.program=p.prog_name AND t.member_id=p.member_id AND t.status=1 
    LEFT JOIN memberTask t1 ON t1.member_id=p.member_id AND t1.status=1 AND t1.program IS NULL 
    GROUP BY mem.id 

感謝を事前に!!!

+0

あなたは言葉で、クエリが何をすることになっていますか?簡単に見ることができるように、メンバーごとに特定の説明が必要です。メンバは多くのプログラムを持つことができ、プログラムは多くのタスクを持つことができ、タスクは多くの記述を持つことができます。しかし、タスクは常にプログラムに関連するとは限りません。だから、メンバーにはプログラムタスクとプログラム以外のタスクがあると言うことができます。これらのうち、メンバーごとに1つのタスク関連の説明が必要なようです。明確にしていただけますか? –

+0

はい、あなたが言ったように:メンバーは多くのプログラムを持つことができ、プログラムは多くのタスクを持つことができます。ここでは、descriptionTableと結合して完全な説明を取得するmemberTaskテーブルからtaskTypeを選択する必要があります。ここでの主な問題は、memberTaskテーブルのmin due_dateフィールドに関してプログラムタスクか非プログラムタスクかどうかをチェックする必要があるので、taskTypeを取得するのにかかる時間です。 –

+0

メンバーのタスクから、最低限の期日のタスクが必要です。このタスクでは、その説明のいずれかに関係なく、 –

答えて

3

クエリを使用している方法は、クエリの実行の複雑さを増すだろうし、それはクロスの多くを通過する必要があります行を結合して必要な行を引き出します。より良い解決策は、最初にターゲットテーブルから必要な行を引き出すことです:memberTask、そしてそれをメインクエリに結合します。次のように

SELECT 
REPLACE(GROUP_CONCAT(COALESCE(CASE WHEN t1.program IS NOT NULL THEN t1.id ELSE NULL END,'')),',','') AS first_dueDate, 
REPLACE(GROUP_CONCAT(COALESCE(CASE WHEN t1.program IS NOT NULL THEN 'first' ELSE 'second' END,'')),',','') AS selectFlag, 
REPLACE(GROUP_CONCAT(COALESCE(CASE WHEN t1.program IS NULL THEN t1.id ELSE NULL END,'')),',','') AS first_dueDate, 
aa.member_id FROM memberTask t1 
INNER JOIN (SELECT due_date,program,aa.member_id,MAX(tsk.id) AS selId FROM memberTask tsk 
INNER JOIN (SELECT t.member_id,t.program,t.task_type,MIN(due_date) AS seldate FROM memberTask t WHERE tsk.status=1 
GROUP BY t.member_id,IFNULL(t.program,''))aa 
ON tsk.member_id=aa.member_id AND IFNULL(tsk.program,'')=IFNULL(aa.program,'') AND tsk.due_date=aa.seldate 
GROUP BY due_date,program,aa.member_id) bb ON t1.id=bb.selId GROUP BY t1.member_id,t1.program 

クエリの内訳は行く:

SELECT t.member_id,t.program,t.task_type,MIN(due_date) AS seldate FROM memberTask t WHERE tsk.status=1 
GROUP BY t.member_id,IFNULL(t.program,'') 

これはMEMBERIDと分期日を選択し、そのプログラムに基づいてレコードを引っ張ってくる最も内側のクエリです。

SELECT due_date,program,aa.member_id,MAX(tsk.id) AS selId FROM memberTask tsk 
INNER JOIN (Inner Most Query)aa 
ON tsk.member_id=aa.member_id AND IFNULL(tsk.program,'')=IFNULL(aa.program,'') AND tsk.due_date=aa.seldate 
GROUP BY due_date,program,aa.member_id 

このクエリは

SELECT 
REPLACE(GROUP_CONCAT(COALESCE(CASE WHEN t1.program IS NOT NULL THEN t1.id ELSE NULL END,'')),',','') AS first_dueDate, 
REPLACE(GROUP_CONCAT(COALESCE(CASE WHEN t1.program IS NOT NULL THEN 'first' ELSE 'second' END,'')),',','') AS selectFlag, 
REPLACE(GROUP_CONCAT(COALESCE(CASE WHEN t1.program IS NULL THEN t1.id ELSE NULL END,'')),',','') AS first_dueDate, 
aa.member_id FROM memberTask t1 
INNER JOIN (second query) bb ON t1.id=bb.selId GROUP BY t1.member_id,t1.program 

同じ期日と行の間で最大のIDを引っ張ってくるこれは、複数の単一の行にデータを漕い全体引っ張ってきます。

この選択されたデータをメインクエリに結合する際に、以下のケースを使用する必要があります。

SELECT * FROM member mem 
INNER JOIN program pro om mem.id=pro.member_id 
LEFT JOIN tmpTable tl ON t1.member_id=mem.member_id AND 
CASE WHEN FIND_IN_SET(pro.`prog_name`, t1.prog_relate_task) THEN t1.program=pro.program AND t1.selectFlag='first' 
ELSE t1.selectFlag='second' END 

これは一般的なケースです。あなたの要件に応じてプルレコードを変更することができます

私はそれが動作するかどうか教えてください?

+0

お問い合わせありがとうございます。クエリの最後のブロック: CASE WHEN FIND_IN_SET(pro.prog_name'、t1.prog_relate_task)t1.program = pro.program AND t1.selectFlag = 'first' ELSE t1.selectFlag = 'second' END は次のとおりです。私は何を待っていたのですか? ありがとうございます。 –

0

私は完全にクエリが何をすべきかを理解しているかどうかはわかりません。私が理解しているのはこれです:各メンバーは、ミニマルデート期日のタスクを(プログラム関連であろうとなかろうと)獲得し、これに対してタスクタイプの説明を取得します。だから、

:メンバーを選択し、LIMITとサブクエリで説明を取得:

select 
    m.id, 
    (
    select d.description 
    from membertask t 
    join descriptiontable d on d.code = t.task_type 
    where t.member_id = m.id 
    order by t.due_date 
    limit 1 
) as description 
from member; 
+0

ここでは、タスクがプログラムタイプであるのか、プログラムタイプでないのかを調べるために、主な問題がありません。タスクテーブルにプログラムがある場合、そのタスクテーブルのデュエートが必要であり、そうでない場合、デュエットはタスクテーブルの非プログラムデータでなければならない。 –

+0

私は分かりません。タスクには期限があります。タスクは、プログラムに関連しているかどうかに関係している可能性があります。私は、プログラムに関係なく、最も古いタスクを選択しています。どのようにこれを違うものにしたいですか?プログラムに関連しないタスクよりもプログラム関連のタスクを優先したいのですか? –

関連する問題