2017-01-28 9 views
2

重複する行を含むc_regsテーブルがあります。私は、form_number列とproperty_name列に索引を作成しました。残念なことに、このクエリは、特にt10とt11の結合を追加することで、o-o-oを完了するのに時間がかかります。それを最適化する方法はありますか?ありがとう。MySQL自己結合クエリを最適化する

select 
    ifnull(x.form_datetime,'') reg_date, 
    ifnull(x.property_value,'') amg_id, 
    x.form_number, 
    x.form_name, 
    x.form_version, 
    ifnull(t1.property_value,'') first_name, 
    ifnull(t2.property_value,'') last_name, 
    ifnull(t3.property_value,'') address, 
    ifnull(t4.property_value,'') address_2, 
    ifnull(t5.property_value,'') city, 
    ifnull(t6.property_value,'') state_code, 
    ifnull(t7.property_value,'') zip, 
    ifnull(t8.property_value,'') phone, 
    ifnull(t9.property_value,'') email, 
    ifnull(t10.property_value,'') registrant_type, 
    t11.property_value auth_type_code 
from 
    (select distinct form_datetime, form_number, form_name, form_version, property_value from c_regs where property_name = 'field.frm_personID') as x 
    inner join (select distinct * from c_regs) as t1 on t1.form_number = x.form_number and t1.property_name = 'field.frm_firstName' 
    inner join (select distinct * from c_regs) as t2 on t2.form_number = x.form_number and t2.property_name = 'field.frm_lastName' 
    inner join (select distinct * from c_regs) as t3 on t3.form_number = x.form_number and t3.property_name = 'field.frm_address' 
    left join (select distinct * from c_regs) as t4 on t4.form_number = x.form_number and t4.property_name = 'field.frm_address2' 
    inner join (select distinct * from c_regs) as t5 on t5.form_number = x.form_number and t5.property_name = 'field.frm_city' 
    inner join (select distinct * from c_regs) as t6 on t6.form_number = x.form_number and t6.property_name = 'field.frm_state' 
    inner join (select distinct * from c_regs) as t7 on t7.form_number = x.form_number and t7.property_name = 'field.frm_zip' 
    inner join (select distinct * from c_regs) as t8 on t8.form_number = x.form_number and t8.property_name = 'field.frm_phone' 
    inner join (select distinct * from c_regs) as t9 on t9.form_number = x.form_number and t9.property_name = 'field.frm_emailAddress' 
    left join (select distinct * from c_regs) as t10 on t10.form_number = x.form_number and t10.property_name = 'field.frm_youAre' 
    inner join (select distinct * from c_regs) as t11 on t11.form_number = x.form_number and t11.property_name = 'field.frm_authType' 
; 

答えて

4

のようなあなたのコード

でUNION句を追加してください。選択リストに一意の制約がある場合、DISTINCTはノーオペレーションになるようにバインドされているので、おそらく必要はありません。重複がある場合、DISTINCTは重複して重複を解消するようにテーブルをソートするため、コストがかかります。

また、この種のデータには多くの自己結合を行うべきではありません。自己結合の各サブクエリは、全体のテーブルを読み取っています。

SELECT form_number, 
    MAX(form_datetime) AS reg_date, 
    MAX(form_name) AS form_name, 
    MAX(form_version) AS form_version, 
    MAX(CASE property_name WHEN 'field.frm_personID' THEN property_value END) AS amg_id, 
    MAX(CASE property_name WHEN 'field.frm_firstName' THEN property_value END) AS first_name, 
    MAX(CASE property_name WHEN 'field.frm_lastName' THEN property_value END) AS last_name, 
    MAX(CASE property_name WHEN 'field.frm_address' THEN property_value END) AS address, 
    MAX(CASE property_name WHEN 'field.frm_address2' THEN property_value END) AS address_2, 
    MAX(CASE property_name WHEN 'field.frm_city' THEN property_value END) AS city, 
    MAX(CASE property_name WHEN 'field.frm_state' THEN property_value END) AS state_code, 
    MAX(CASE property_name WHEN 'field.frm_zip' THEN property_value END) AS zip, 
    MAX(CASE property_name WHEN 'field.frm_phone' THEN property_value END) AS phone, 
    MAX(CASE property_name WHEN 'field.frm_emailAddress' THEN property_value END) AS email, 
    MAX(CASE property_name WHEN 'field.frm_youAre' THEN property_value END) AS registrant_type, 
    MAX(CASE property_name WHEN 'field.frm_authType' THEN property_value END) AS auth_type_code 
FROM c_regs 
GROUP BY form_number; 

説明:GROUP BYは、所与FORM_NUMBERためのすべての行が一つのグループとして扱われることになり、その結果、グループごとに1つの行を有することになります。

GROUP BYで名前が付けられていない他のすべての列は、グループ化関数内にある必要があります。私はMAX()を選びました。私は、フォームのdatetime、名前、およびバージョンのグループごとに1つの別個の値しかないと仮定します。

プロパティの場合、MAX()関数内に式を挿入して、プロパティが特定の値を持つ行でのみ値を返します。他の行では、式はNULLであり、MAX()は無視します。

このようにして、自己結合またはDISTINCT修飾子を使用することなく、結果を得ることができます。クエリは1回だけテーブルをスキャンし、より高速にする必要があります。

+1

ニース! 35分から0.01秒まで。 :)私は次にそれを勉強するつもりなので、何が起こっているのか正確に理解しています。手伝ってくれてどうもありがとう! – demisx

+0

喜んで助けてください! Stack Overflowでは、あなたを助けてくれた回答をupvoteまたは受け入れることが慣例であることに注意してください。 :-) –

+0

私の経験では、このような種類のクエリ(読みやすい)は、選択肢よりも部分的に遅いです。 – Strawberry

1

これらの結合はすべて必要ありません。私の最適化では、データはカラムではなく行で返されます。あなたが特定の形式ではなく、それらのすべてをしたい場合は

は(私はこれを実行し、それが最初にテストしていない)

SELECT 
    ifnull(x.form_datetime,'') reg_date, 
    ifnull(x.property_value,'') amg_id, 
    x.form_number, 
    x.form_name, 
    x.form_version, 
    x.property_name, 
    x.property_value 
FROM c_regs x 
WHERE x.property_name IN (
    'field.frm_firstName', 
    'field.frm_lastName', 
    'field.frm_address', 
    ... 
) 
AND x.form_number = 'the form id' 
GROUP BY x.form_number, x.property_name 
ORDER BY x.form_number ASC; 

ANDのみ必要です。 (私が示唆している)

あなた自身も質問してください:条件にフィールド名を付ける必要がありますか?私のクエリをサブクエリとして使用し、以前と同じように各フィールドを別の結合なしでマージすることができます。

0

はあなたがSELECT DISTINCTすべての時間を使うべきではありません

SELECT ID, NAME, AMOUNT, DATE 
FROM CUSTOMERS 
LEFT JOIN ORDERS 
ON CUSTOMERS.ID = ORDERS.CUSTOMER_ID 
UNION 
SELECT ID, NAME, AMOUNT, DATE 
FROM CUSTOMERS 
RIGHT JOIN ORDERS 
ON CUSTOMERS.ID = ORDERS.CUSTOMER_ID; 
2

多くの自己結合が有害であるというBKの主張は誤解を招きます。

次の12の属性を持つ各よう、万個のエンティティを含んで成るEAVデータセットを考えてみましょう:

DROP TABLE IF EXISTS my_table; 

CREATE TABLE my_table 
(entity INT NOT NULL 
,attribute INT NOT NULL 
,value INT NOT NULL 
,PRIMARY KEY(entity,attribute) 
); 

INSERT INTO my_table VALUES 
(1,101,RAND()*100), 
(1,102,RAND()*100), 
(1,103,RAND()*100), 
(1,104,RAND()*100), 
(1,105,RAND()*100), 
(1,106,RAND()*100), 
(1,107,RAND()*100), 
(1,108,RAND()*100), 
(1,109,RAND()*100), 
(1,110,RAND()*100), 
(1,111,RAND()*100), 
(1,112,RAND()*100); 

をこの初期シードで、私は急速に残りの部分を埋めるために、整数(0-9)のテーブルを使用することができますテーブルの...

INSERT IGNORE INTO my_table SELECT i4.i*1000+i3.i*100+i2.i*10+i1.i+1, attribute, RAND()*100 FROM my_table,ints i1, ints i2, ints i3, ints i4; 

ビルのクエリ...

SELECT SQL_NO_CACHE a.entity 
    , MAX(CASE WHEN attribute = 101 THEN value END) x101 
    , MAX(CASE WHEN attribute = 102 THEN value END) x102 
    , MAX(CASE WHEN attribute = 103 THEN value END) x103 
    , MAX(CASE WHEN attribute = 104 THEN value END) x104 
    , MAX(CASE WHEN attribute = 105 THEN value END) x105 
    , MAX(CASE WHEN attribute = 106 THEN value END) x106 
    , MAX(CASE WHEN attribute = 107 THEN value END) x107 
    , MAX(CASE WHEN attribute = 108 THEN value END) x108 
    , MAX(CASE WHEN attribute = 109 THEN value END) x109 
    , MAX(CASE WHEN attribute = 110 THEN value END) x110 
    , MAX(CASE WHEN attribute = 111 THEN value END) x111 
    , MAX(CASE WHEN attribute = 112 THEN value END) x112 
    FROM my_table a 
GROUP 
    BY a.entity; 

+--------+------+------+------+------+------+------+------+------+------+------+------+------+ 
| entity | x101 | x102 | x103 | x104 | x105 | x106 | x107 | x108 | x109 | x110 | x111 | x112 | 
+--------+------+------+------+------+------+------+------+------+------+------+------+------+ 
|  1 | 78 | 8 | 4 | 95 | 66 | 43 | 16 | 51 | 9 | 89 | 20 | 33 | 
... 
| 9998 | 61 | 72 | 67 | 20 | 23 | 10 | 31 | 37 | 69 | 18 | 24 | 32 | 
| 9999 | 67 | 91 | 32 | 58 | 77 | 81 | 61 | 22 | 75 | 65 | 91 | 42 | 
| 10000 | 52 | 38 | 56 | 32 | 14 | 77 | 10 | 99 | 70 | 70 | 82 | 13 | 
+--------+------+------+------+------+------+------+------+------+------+------+------+------+  
10000 rows in set (0.20 sec) 

代替...だから

SELECT SQL_NO_CACHE a.entity 
    , a.value x101 
    , b.value x102 
    , c.value x103 
    , d.value x104 
    , e.value x105 
    , f.value x106 
    , g.value x107 
    , h.value x108 
    , i.value x109 
    , j.value x110 
    , k.value x111 
    , l.value x112 
    FROM my_table a 
    LEFT JOIN my_table b ON b.entity = a.entity AND b.attribute = 102 
    LEFT JOIN my_table c ON c.entity = a.entity AND c.attribute = 103 
    LEFT JOIN my_table d ON d.entity = a.entity AND d.attribute = 104 
    LEFT JOIN my_table e ON e.entity = a.entity AND e.attribute = 105 
    LEFT JOIN my_table f ON f.entity = a.entity AND f.attribute = 106 
    LEFT JOIN my_table g ON g.entity = a.entity AND g.attribute = 107 
    LEFT JOIN my_table h ON h.entity = a.entity AND h.attribute = 108 
    LEFT JOIN my_table i ON i.entity = a.entity AND i.attribute = 109 
    LEFT JOIN my_table j ON j.entity = a.entity AND j.attribute = 110 
    LEFT JOIN my_table k ON k.entity = a.entity AND k.attribute = 111 
    LEFT JOIN my_table l ON l.entity = a.entity AND l.attribute = 112 
    WHERE a.attribute = 101; 

+--------+------+------+------+------+------+------+------+------+------+------+------+------+ 
| entity | x101 | x102 | x103 | x104 | x105 | x106 | x107 | x108 | x109 | x110 | x111 | x112 | 
+--------+------+------+------+------+------+------+------+------+------+------+------+------+ 
|  1 | 78 | 8 | 4 | 95 | 66 | 43 | 16 | 51 | 9 | 89 | 20 | 33 | 
... 
| 9998 | 61 | 72 | 67 | 20 | 23 | 10 | 31 | 37 | 69 | 18 | 24 | 32 | 
| 9999 | 67 | 91 | 32 | 58 | 77 | 81 | 61 | 22 | 75 | 65 | 91 | 42 | 
| 10000 | 52 | 38 | 56 | 32 | 14 | 77 | 10 | 99 | 70 | 70 | 82 | 13 | 
+--------+------+------+------+------+------+------+------+------+------+------+------+------+ 
10000 rows in set (0.23 sec) 

、ビルのクエリを分別高速です。ただし、同じ種類の属性(同じ数の結合数)を維持しながら、求められるエンティティの数を減らすとすぐに、代替クエリは同じ種類のマージンに近づくことによってBillを追い抜くことができます。

Bill's WHERE a.entity <= 5000とクエリがWHERE a.entity <= 5000と代替だからそれは本当に加入の数ではなく、間の違いは、インデックスの勤勉な使用ではありません

| 4998 | 59 | 55 | 93 | 48 | 72 | 32 | 38 | 36 | 6 | 82 | 23 | 62 | 
    | 4999 | 23 | 10 | 11 | 29 | 69 | 67 | 92 | 72 | 25 | 49 | 79 | 48 | 
    | 5000 | 39 | 86 | 77 | 0 | 30 | 38 | 48 | 54 | 9 | 97 | 25 | 54 | 
    +--------+------+------+------+------+------+------+------+------+------+------+------+------+ 
5000 rows in set (0.11 sec) 

を追加

| 4998 | 59 | 55 | 93 | 48 | 72 | 32 | 38 | 36 | 6 | 82 | 23 | 62 | 
    | 4999 | 23 | 10 | 11 | 29 | 69 | 67 | 92 | 72 | 25 | 49 | 79 | 48 | 
    | 5000 | 39 | 86 | 77 | 0 | 30 | 38 | 48 | 54 | 9 | 97 | 25 | 54 | 
    +--------+------+------+------+------+------+------+------+------+------+------+------+------+ 
5000 rows in set (0.12 sec) 

を追加しました遅くて速いクエリです。

+0

よくできました!また、両方のソリューションは元のクエリよりも桁違いに優れており、それぞれが完全なテーブルスキャンを行う一連のサブクエリに参加します。 –

+0

冗談なし。君たちありがとう。私は将来の使用のためにこれらの両方のクエリを保存します。私たちはこの種のクエリをたくさん使用します。 – demisx