MySQL勉強会 クエリチューニング編

4,131 views
3,890 views

Published on

社内で実施したMySQLの勉強会資料です。
クエリチューニングメインの内容になっています。
B-TREEインデックスやソートの仕組み、相関サブクエリなんかを取り扱っています。

Published in: Technology
0 Comments
26 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,131
On SlideShare
0
From Embeds
0
Number of Embeds
45
Actions
Shares
0
Downloads
44
Comments
0
Likes
26
Embeds 0
No embeds

No notes for slide

MySQL勉強会 クエリチューニング編

  1. 1. MicroAdマイクロアド薮下 和弥システム開発部MySQL 勉強会クエリチューニング編
  2. 2. はじめにこんな考え方をしていませんか?
  3. 3.
  4. 4. 当勉強会で…これらの考え方が変わり、クエリチューニングの見解が広がります!
  5. 5. 1章 対話的にクエリを作る2章 オプティマイザの判断3章 サブクエリは「データ」の集合4章 ソートとインデックス5章 インデックスは万能ではない6章 相関サブクエリは諸刃の剣休憩
  6. 6. 1章 対話的にクエリを作る2章 オプティマイザの判断3章 サブクエリは「データ」の集合4章 ソートとインデックス5章 インデックスは万能ではない6章 相関サブクエリは諸刃の剣休憩
  7. 7. 1章 対話的にクエリを作るCity:都市テーブル+-------------+------------+----------+------+-----+| ColumnID | ColumnName | Type | Null | Key |+-------------+------------+----------+------+-----+| ID | 都市ID | int(11) | NO | PRI || Name | 都市名 | char(35) | NO | || CountryCode | 国コード | char(3) | NO | MUL || District | 地区 | char(20) | NO | || Population | 都市人口 | int(11) | NO | MUL |+-------------+------------+----------+------+-----++----------------+------------------+---------------------------------------------------------------------------------------+------+-----+| ColumnID | ColumnName | Type | Null | Key |+----------------+------------------+---------------------------------------------------------------------------------------+------+-----+| Code | 国コード | char(3) | NO | PRI || Name | 国名 | char(52) | NO | || Continent | 大陸 | enum(Asia,Europe,North America,Africa,Oceania,Antarctica,South America) | NO | || Region | 地帯 | char(26) | NO | || SurfaceArea | 国土面積 | float(10,2) | NO | || IndepYear | 独立年 | smallint(6) | YES | || Population | 国人口 | int(11) | NO | || LifeExpectancy | 平均寿命 | float(3,1) | YES | || GNP | 国民総生産 | float(10,2) | YES | || GNPOld | 国民総生産(過去) | float(10,2) | YES | || LocalName | 国名(ローカル) | char(45) | NO | || GovernmentForm | 政治体系 | char(45) | NO | || HeadOfState | 国家元首 | char(60) | YES | || Capital | 首都コード | int(11) | YES | || Code2 | 国コード(略) | char(2) | NO | |+----------------+------------------+---------------------------------------------------------------------------------------+------+-----+Country:国テーブル件数:4079件数:239当勉強会ではMySQLのサンプルデータベース内のテーブルを使用し、実際にクエリを組んで見解を広げて頂きます。Code・・Country・CountryCode・City
  8. 8. Population / 2+------------+| Population |+------------+| 4848150 || 3990115 || 4990810 |+------------+1章 対話的にクエリを作る抽出項目都市名国コード都市人口都市人口の半数が、瀋陽の人口よりも多い都市抽出条件クエリSELECTCty1.Name,Cty1.CountryCode,Cty1.PopulationFROM City Cty1WHERE Cty.Population / 2 > (SELECTCty2.PopulationFROM City Cty2WHERE Cty2.Name = Shenyang);City(都市テーブル)+-------------+------------+----------+-----+| ColumnID | ColumnName | Type | Key |+-------------+------------+----------+-----+| ID | 都市ID | int(11) | PRI || Name | 都市名 | char(35) | || CountryCode | 国コード | char(3) | MUL || District | 地区 | char(20) | || Population | 都市人口 | int(11) | MUL |+-------------+------------+----------+-----+テーブル抽出イメージCty1(City)+------+----------+-------------+----------+------------+| ID | Name | CountryCode | District | Population |+------+----------+-------------+----------+------------+| 1890 | Shanghai | CHN | Shanghai | 9696300 || 1532 | Tokyo | JPN | Tokyo-to | 7980230 || 2331 | Seoul | KOR | Seoul | 9981620 |+------+----------+-------------+----------+------------+瀋陽の人口+------------+| Population |+------------+| 4265200 |+------------+Cty1(City)+------+----------+-------------+----------+------------+| ID | Name | CountryCode | District | Population |+------+----------+-------------+----------+------------+| 1890 | Shanghai | CHN | Shanghai | 9696300 |○| 1532 | Tokyo | JPN | Tokyo-to | 7980230 || 2331 | Seoul | KOR | Seoul | 9981620 |○+------+----------+-------------+----------+------------+Cty1(City)+------+----------+-------------+----------+------------+| ID | Name | CountryCode | District | Population |+------+----------+-------------+----------+------------+| 1890 | Shanghai | CHN | Shanghai | 9696300 |○| 1532 | Tokyo | JPN | Tokyo-to | 7980230 || 2331 | Seoul | KOR | Seoul | 9981620 |○+------+----------+-------------+----------+------------+Population / 2+------------+| Population |+------------+| 4848150 || 3990115 || 4990810 |+------------+
  9. 9. 1章 対話的にクエリを作る完?+----------+-------------+------------+| Name | CountryCode | Population |+----------+-------------+------------+| Shanghai | CHN | 9696300 || Seoul | KOR | 9981620 |+----------+-------------+------------+実 行 結 果
  10. 10. 1章 対話的にクエリを作るちょっと待った速いの?このクエリ
  11. 11. 1章 対話的にクエリを作るmysql> SELECT … WHERE Cty2.Name = Shenyang);・・・8 rows in set (0.00 sec)速いッ!
  12. 12. 1章 対話的にクエリを作る…それだけでは駄目です。実行計画を取得してください。
  13. 13. 1章 対話的にクエリを作る実行計画とは、問い合わせ情報(クエリ)を基に、MySQLが内部的に立てる実行の計画(手順)です。※MySQL 5.6からUPDATE、DELETE、INSERTも実行計画取得が可能EXPLAIN select_query;実行計画取得コマンド言わば、なのです。MySQLの意思表示
  14. 14. 1章 対話的にクエリを作る先程作成したクエリの実行計画を確認してみましょう。+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| 1 | PRIMARY | Cty1 | ALL | NULL | NULL | NULL | NULL | 4051 | Using where || 2 | SUBQUERY | Cty2 | ALL | NULL | NULL | NULL | NULL | 4051 | Using where |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+EXPLAIN SELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population / 2 > (SELECTCty2.PopulationFROM City Cty2WHERE Cty2.Name = Shenyang);クエリ実行計画+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| 1 | PRIMARY | Cty1 | ALL | NULL | NULL | NULL | NULL | 4051 | Using where || 2 | SUBQUERY | Cty2 | ALL | NULL | NULL | NULL | NULL | 4051 | Using where |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+INDEXが使われてない!テーブルスキャン!!外部クエリサブクエリ
  15. 15. 1章 対話的にクエリを作る何がダメなの? |ω・)
  16. 16. 1章 対話的にクエリを作る例えばこんなクエリ。SELECT * FROM City WHERE Name = Tokyo; City(都市テーブル) 件数:4079+-------------+------------+-----+| ColumnID | ColumnName | Key |+-------------+------------+-----+| ID | 都市ID | PRI || Name | 都市名 | || CountryCode | 国コード | MUL || District | 地区 | || Population | 都市人口 | MUL |+-------------+------------+-----+row75row1row1001row561row314row245row843row48row1831 row181row468row188row2188row821row87row987row456row6row236row4000row813row3row30row209row131row2430row2120row21row2901row333row713row3557row1684row338row3388row1088row10row1540row3140row1059row193row412row198row15row681row1926row1985row2792row961row461row1563row3154row1863row731row462row1462row3311row1991row1535row501row852row3841row100row1091row491row481row584row522row521row64row487row983row1616row3731Cityrow737row1379row3838row1103row588row519row819row4009row12Cityの行を一つ一つを確認して、NameがTokyoの行を探してください。
  17. 17. 1章 対話的にクエリを作るこれが、テーブルスキャン。
  18. 18. 1章 対話的にクエリを作るINDEXを付けるとどうなるの? |ω・)
  19. 19. 1章 対話的にクエリを作るSELECT * FROM City WHERE Name = Tokyo; City(都市テーブル) 件数:4079+-------------+------------+-----+| ColumnID | ColumnName | Key |+-------------+------------+-----+| ID | 都市ID | PRI || Name | 都市名 | MUL || CountryCode | 国コード | MUL || District | 地区 | || Population | 都市人口 | MUL |+-------------+------------+-----+row1863CityNameカラムのINDEX情報TokyoA - KL - ZA - DE - GH - IL - OP - RS - ZPekingPusanQazvinRotterdamRomaLondonLiverpoolMexicoNew YorkOkinawaSydneyTokyoWashingtonValeraZelenograd… … …A - KL - ZL - OP - RS - ZSydneyTokyoWashingtonValeraZelenograd
  20. 20. 1章 対話的にクエリを作るこれが、INDEXッ!!
  21. 21. +-------------+------------+----------+------+-----+| ColumnID | ColumnName | Type | Null | Key |+-------------+------------+----------+------+-----+| ID | 都市ID | int(11) | NO | PRI || Name | 都市名 | char(35) | NO | || CountryCode | 国コード | char(3) | NO | MUL || District | 地区 | char(20) | NO | || Population | 都市人口 | int(11) | NO | MUL |+-------------+------------+----------+------+-----+1章 対話的にクエリを作るMySQLには是非INDEXを使って頂きたい。サブクエリからチューニングしてみます。都市人口の半分が瀋陽の人口よりも多い都市抽出仕様City:都市テーブル+-------------+------------+----------+------+-----+| ColumnID | ColumnName | Type | Null | Key |+-------------+------------+----------+------+-----+| ID | 都市ID | int(11) | NO | PRI || Name | 都市名 | char(35) | NO | || CountryCode | 国コード | char(3) | NO | MUL || District | 地区 | char(20) | NO | || Population | 都市人口 | int(11) | NO | MUL |+-------------+------------+----------+------+-----+サブクエリSELECTCty1.Name,Cty1.CountryCode,Cty1.PopulationFROM City Cty1WHERE Cty1.Population / 2 > (SELECTCty2.PopulationFROM City Cty2WHERE Cty2.Name = Shenyang);都市人口の半分が中国の瀋陽の人口よりも多い都市瀋陽が中国の都市であることは明確であり、抽出条件に国コードが追加されても問題はない。SELECTCty1.Name,Cty1.CountryCode,Cty1.PopulationFROM City Cty1WHERE Cty1.Population / 2 > (SELECTCty2.PopulationFROM City Cty2WHEREAND Cty2.Name = Shenyang);SELECTCty1.Name,Cty1.CountryCode,Cty1.PopulationFROM City Cty1WHERE Cty1.Population / 2 > (SELECTCty2.PopulationFROM City Cty2WHERE Cty2.CountryCode = CHNAND Cty2.Name = Shenyang);
  22. 22. +----+-------------+-------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| 1 | PRIMARY | Cty1 | ALL | NULL | NULL | NULL | NULL | 4051 | Using where || 2 | SUBQUERY | Cty2 | ALL | NULL | NULL | NULL | NULL | 4051 | Using where |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+1章 対話的にクエリを作る修正後の実行計画は…+----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+| 1 | PRIMARY | Cty1 | ALL | NULL | NULL | NULL | NULL | 4051 | Using where || 2 | SUBQUERY | Cty2 | ref | CountryCode | CountryCode | 3 | const | 363 | Using where |+----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+SELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population / 2 > (SELECTCty2.PopulationFROM City Cty2WHERE Cty2.CountryCode = CHNAND Cty2.Name = Shenyang);クエリチューニング前の実行計画サブクエリの検索条件にCountryCodeを指定したことで、INDEXを利用した検索が可能に!チューニング後の実行計画
  23. 23. 1章 対話的にクエリを作る次は外部クエリ。+----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+| 1 | PRIMARY | Cty1 | ALL | NULL | NULL | NULL | NULL | 4051 | Using where || 2 | SUBQUERY | Cty2 | ref | CountryCode | CountryCode | 3 | const | 363 | Using where |+----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+City:都市テーブル+-------------+------------+----------+------+-----+| ColumnID | ColumnName | Type | Null | Key |+-------------+------------+----------+------+-----+| ID | 都市ID | int(11) | NO | PRI || Name | 都市名 | char(35) | NO | || CountryCode | 国コード | char(3) | NO | MUL || District | 地区 | char(20) | NO | || Population | 都市人口 | int(11) | NO | MUL |+-------------+------------+----------+------+-----+クエリ実行計画SELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population / 2 > (SELECTCty2.PopulationFROM City Cty2WHERE Cty2.CountryCode = CHNAND Cty2.Name = Shenyang);あれ?!INDEXは?!SELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population / 2 > (SELECTCty2.PopulationFROM City Cty2WHERE Cty2.CountryCode = CHNAND Cty2.Name = Shenyang);+-------------+------------+----------+------+-----+| ColumnID | ColumnName | Type | Null | Key |+-------------+------------+----------+------+-----+| ID | 都市ID | int(11) | NO | PRI || Name | 都市名 | char(35) | NO | || CountryCode | 国コード | char(3) | NO | MUL || District | 地区 | char(20) | NO | || Population | 都市人口 | int(11) | NO | MUL |+-------------+------------+----------+------+-----++----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+| 1 | PRIMARY | Cty1 | ALL | NULL | NULL | NULL | NULL | 4051 | Using where || 2 | SUBQUERY | Cty2 | ref | CountryCode | CountryCode | 3 | const | 363 | Using where |+----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+
  24. 24. 1章 対話的にクエリを作るWHERE句内をよく見てください。クエリSELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population / 2 > (SELECTCty2.PopulationFROM City Cty2WHERE Cty2.CountryCode = CHNAND Cty2.Name = Shenyang);SELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population / 2 > (SELECTCty2.PopulationFROM City Cty2WHERE Cty2.CountryCode = CHNAND Cty2.Name = Shenyang);索引列に対して演算処理を行っている場合、オプティマイザはINDEXを使用することが出来ないのです。
  25. 25. 1章 対話的にクエリを作るではどうするか。都市人口の半分が中国瀋陽の人口よりも多い都市抽出仕様SELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population / 2 > (SELECTCty2.PopulationFROM City Cty2WHERE Cty2.CountryCode = CHNAND Cty2.Name = Shenyang);SELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population > (SELECTCty2.PopulationFROM City Cty2WHERE Cty2.CountryCode = CHNAND Cty2.Name = Shenyang) * 2;= 都市人口が中国瀋陽の倍の人口よりも多い都市抽出の仕様に影響なしクエリ
  26. 26. +----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+| 1 | PRIMARY | Cty1 | ALL | NULL | NULL | NULL | NULL | 4051 | Using where || 2 | SUBQUERY | Cty2 | ref | CountryCode | CountryCode | 3 | const | 363 | Using where |+----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+1章 対話的にクエリを作る修正後の実行計画は…+----+-------------+-------+-------+---------------+-------------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+-------+---------------+-------------+---------+------+------+-------------+| 1 | PRIMARY | Cty1 | range | Population | Population | 4 | NULL | 8 | Using where || 2 | SUBQUERY | Cty2 | ref | CountryCode | CountryCode | 3 | | 363 | Using where |+----+-------------+-------+-------+---------------+-------------+---------+------+------+-------------+SELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population > (SELECTCty2.PopulationFROM City Cty2WHERE Cty2.CountryCode = CHNAND Cty2.Name = Shenyang) * 2;クエリクエリチューニング前実行計画索引列の演算処理を、右辺に移すことでINDEXを利用した範囲検索が可能に!クエリチューニング後実行計画
  27. 27. 1章 対話的にクエリを作る仕上げの実行結果確認+----------+-------------+------------+| Name | CountryCode | Population |+----------+-------------+------------+| Shanghai | CHN | 9696300 || Seoul | KOR | 9981620 |+----------+-------------+------------+実 行 結 果
  28. 28. 1章 対話的にクエリを作るちなみに・・・
  29. 29. 1章 対話的にクエリを作る索引列を指定しても、オプティマイザがINDEXを使用できないパターンは他にも存在します。WHERE Index_Column IS NULLNULL述語を使用WHERE SUBSTRING(Index_Column, 1, 3) = abcSQL関数を使用WHERE Index_Column <> abc否定形での条件指定WHERE Index_Column = abc OR Index_Column = defORでの条件指定(INへの置き換えで対応可)WHERE Index_Column LIKE %abc%中間一致、後方一致でのLIKE述語を使用(前方一致は使用可能)・・・
  30. 30. 1章 まとめクエリ作成後は、必ず実行計画を取得すること!
  31. 31. 1章 対話的にクエリを作る2章 オプティマイザの判断3章 サブクエリは「データ」の集合4章 ソートとインデックス5章 インデックスは万能ではない6章 相関サブクエリは諸刃の剣休憩
  32. 32. 2章 オプティマイザの判断人口が400000人超えの都市 都市名都市人口SELECT Cty.Name,Cty.PopulationFROM City CtyWHERE Cty.Population > 400000;抽出条件 抽出項目クエリテーブルCity(都市テーブル)+-------------+------------+----------+-----+| ColumnID | ColumnName | Type | Key |+-------------+------------+----------+-----+| ID | 都市ID | int(11) | PRI || Name | 都市名 | char(35) | || CountryCode | 国コード | char(3) | MUL || District | 地区 | char(20) | || Population | 都市人口 | int(11) | MUL |+-------------+------------+----------+-----+抽出イメージCty+------+------------+-------------+----------------+------------+| ID | Name | CountryCode | District | Population |+------+------------+-------------+----------------+------------+| 135 | Canberra | AUS | Capital Region | 322723 || 1570 | Gifu | JPN | Gifu | 408007 || 1822 | Ottawa | CAN | Ontario | 335277 || 2318 | Pyongyang | PRK | Pyongyang-si | 2484000 || 3209 | Bratislava | SVK | Bratislava | 448292 || 3831 | Atlanta | USA | Georgia | 416474 |+------+------------+-------------+----------------+------------+Cty+------+------------+-------------+----------------+------------+| ID | Name | CountryCode | District | Population |+------+------------+-------------+----------------+------------+| 135 | Canberra | AUS | Capital Region | 322723 || 1570 | Gifu | JPN | Gifu | 408007 |○| 1822 | Ottawa | CAN | Ontario | 335277 || 2318 | Pyongyang | PRK | Pyongyang-si | 2484000 |○| 3209 | Bratislava | SVK | Bratislava | 448292 |○| 3831 | Atlanta | USA | Georgia | 416474 |○+------+------------+-------------+----------------+------------+Cty+------+------------+-------------+----------------+------------+| ID | Name | CountryCode | District | Population |+------+------------+-------------+----------------+------------+| 135 | Canberra | AUS | Capital Region | 322723 || 1570 | Gifu | JPN | Gifu | 408007 |○| 1822 | Ottawa | CAN | Ontario | 335277 || 2318 | Pyongyang | PRK | Pyongyang-si | 2484000 |○| 3209 | Bratislava | SVK | Bratislava | 448292 |○| 3831 | Atlanta | USA | Georgia | 416474 |○+------+------------+-------------+----------------+------------+
  33. 33. 2章 オプティマイザの判断今回はイケる気がする (`・ω ・´)
  34. 34. 2章 オプティマイザの判断+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| 1 | SIMPLE | Cty | ALL | Population | NULL | NULL | NULL | 4051 | Using where |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+EXPLAIN SELECT Cty.CountryCode,Cty.Name,Cty.PopulationFROM City CtyWHERE Cty.Population > 400000;…あれっ!?実行計画実行計画は…
  35. 35. +----+-------------+-------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| 1 | SIMPLE | Cty | ALL | Population | NULL | NULL | NULL | 4051 | Using where |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+2章 オプティマイザの判断possible_keysを見てください。INDEX「Population」は使える状況であることがわかります。+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| 1 | SIMPLE | Cty | ALL | Population | NULL | NULL | NULL | 4051 | Using where |+----+-------------+-------+------+---------------+------+---------+------+------+-------------++----+-------------+-------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| 1 | PRIMARY | Cty1 | ALL | NULL | NULL | NULL | NULL | 4051 | Using where || 2 | SUBQUERY | Cty2 | ALL | NULL | NULL | NULL | NULL | 4051 | Using where |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+※1章のチューニング前の実行計画
  36. 36. 2章 オプティマイザの判断では、なぜ?
  37. 37. 2章 オプティマイザの判断オプティマイザがINDEXを使用するよりも、テーブルスキャンをした方が効率が良いと判断したのです。※取得するデータの量が表全体の5%~15%以下(目安)の場合にインデックスを使用
  38. 38. 2章 オプティマイザの判断試しに人口600000人超え(表全体の約10%)の都市を抽出してみます。+----+-------------+-------+-------+---------------+------------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+-------+---------------+------------+---------+------+------+-------------+| 1 | SIMPLE | Cty | range | Population | Population | 4 | NULL | 428 | Using where |+----+-------------+-------+-------+---------------+------------+---------+------+------+-------------+EXPLAIN SELECT Cty.CountryCode,Cty.Name,Cty.PopulationFROM City CtyWHERE Cty.Population > 600000;この条件の場合は、INDEXを利用した方が効率が良いと判断したようです。実行計画
  39. 39. 2章 まとめINDEXを使えないのか、使わないのか。適切に判断し、適切にアプローチを。
  40. 40. 1章 対話的にクエリを作る2章 オプティマイザの判断3章 サブクエリは「データ」の集合4章 ソートとインデックス5章 インデックスは万能ではない6章 相関サブクエリは諸刃の剣休憩
  41. 41. 3章 サブクエリは「データ」の集合SELECTCtr.Name CountryName,Sub.Name CityName,Sub.PopulationFROM Country CtrLEFT JOIN (SELECTCty.CountryCode,Cty.Name,Cty.PopulationFROM City CtyWHERE Cty.Population > 400000) SubON Ctr.Code = Sub.CountryCode;City(都市テーブル)+-------------+------------+-----+| ColumnID | ColumnName | Key |+-------------+------------+-----+| ID | 都市ID | PRI || Name | 都市名 | || CountryCode | 国コード | MUL || District | 地区 | || Population | 都市人口 | MUL |+-------------+------------+-----+Country(国テーブル)+----------+---------------+-----+| ColumnID | ColumnName | Key |+----------+---------------+-----+| Code | 国コード | PRI || Name | 国名 | |==============省略================全ての国名の一覧を表示。各国に人口が400000人以上の都市が存在する場合、その各都市の名称と人口の情報を付与する。存在しない場合はNULLを表示する。国名都市名都市人口抽出条件 抽出項目クエリテーブル抽出イメージCtr+------+---------------+| Code | Name |+------+---------------+| JPN | Japan || KOR | South Korea || SVK | Slovakia || USA | United States |+------+---------------+Cty+------------+-------------+------------+| Name | CountryCode | Population |+------------+-------------+------------+| Gifu | JPN | 408007 || Pyongyang | PRK | 2484000 || Bratislava | SVK | 448292 || Atlanta | USA | 416474 |+------------+-------------+------------+Ctr LEFT JOIN Sub+---------------+------------+------------+| CountryName | CityName | Population |+---------------+------------+------------+| Japan | Gifu | 408007 || South Korea | NULL | NULL || Slovakia | Bratislava | 448292 || United States | Atlanta | 416474 |+---------------+------------+------------+Sub+------------+-------------+------------+| Name | CountryCode | Population |+------------+-------------+------------+| Gifu | JPN | 408007 |○| Pyongyang | PRK | 2484000 || Bratislava | SVK | 448292 |○| Atlanta | USA | 416474 |○+------------+-------------+------------++ =
  42. 42. 3章 サブクエリは「データ」の集合クエリの実行計画を確認してみます。+----+-------------+------------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+------+---------------+------+---------+------+------+-------------+| 1 | PRIMARY | Ctr | ALL | NULL | NULL | NULL | NULL | 241 | || 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 737 | || 2 | DERIVED | Cty | ALL | Population | NULL | NULL | NULL | 4051 | Using where |+----+-------------+------------+------+---------------+------+---------+------+------+-------------+EXPLAIN SELECTCtr.Name CountryName,Sub.Name CityName,Sub.PopulationFROM Country CtrLEFT JOIN (SELECTCty.CountryCode,Cty.Name,Cty.PopulationFROM City CtyWHERE Cty.Population > 400000) SubON Ctr.Code = Sub.CountryCode;+----+-------------+------------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+------+---------------+------+---------+------+------+-------------+| 1 | PRIMARY | Ctr | ALL | NULL | NULL | NULL | NULL | 241 | || 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 737 | || 2 | DERIVED | Cty | ALL | Population | NULL | NULL | NULL | 4051 | Using where |+----+-------------+------------+------+---------------+------+---------+------+------+-------------+結合キーとして、INDEXが使われていません。実行計画
  43. 43. derived23章 サブクエリは「データ」の集合図解するとこんな感じです。+----+-------------+------------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+------+---------------+------+---------+------+------+-------------+| 1 | PRIMARY | Ctr | ALL | NULL | NULL | NULL | NULL | 241 | || 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 737 | || 2 | DERIVED | Cty | ALL | Population | NULL | NULL | NULL | 4051 | Using where |+----+-------------+------------+------+---------------+------+---------+------+------+-------------+CtyPKINDEXCtrサブクエリにて抽出した集合にはPKやINDEXの情報は含まれない。Ctrを基準にderived2とJOINPKINDEX※基準になるテーブルのINDEXは使われません。
  44. 44. 3章 サブクエリは「データ」の集合ちょっと、振り返ります。
  45. 45. 3章 サブクエリは「データ」の集合SELECTCtr.Name CountryName,Sub.Name CityName,Sub.PopulationFROM Country CtrLEFT JOIN (SELECTCty.CountryCode,Cty.Name,Cty.PopulationFROM City CtyWHERE Cty.Population > 400000) SubON Ctr.Code = Sub.CountryCode;City(都市テーブル)+-------------+------------+-----+| ColumnID | ColumnName | Key |+-------------+------------+-----+| ID | 都市ID | PRI || Name | 都市名 | || CountryCode | 国コード | MUL || District | 地区 | || Population | 都市人口 | MUL |+-------------+------------+-----+Country(国テーブル)+----------+---------------+-----+| ColumnID | ColumnName | Key |+----------+---------------+-----+| Code | 国コード | PRI || Name | 国名 | |==============省略================クエリテーブル抽出イメージCtr+------+---------------+| Code | Name |+------+---------------+| JPN | Japan || KOR | South Korea || SVK | Slovakia || USA | United States |+------+---------------+Cty+------------+-------------+------------+| Name | CountryCode | Population |+------------+-------------+------------+| Gifu | JPN | 408007 || Pyongyang | PRK | 2484000 || Bratislava | SVK | 448292 || Atlanta | USA | 416474 |+------------+-------------+------------+Ctr LEFT JOIN Sub+---------------+------------+------------+| CountryName | CityName | Population |+---------------+------------+------------+| Japan | Gifu | 408007 || South Korea | NULL | NULL || Slovakia | Bratislava | 448292 || United States | Atlanta | 416474 |+---------------+------------+------------+Sub+------------+-------------+------------+| Name | CountryCode | Population |+------------+-------------+------------+| Gifu | JPN | 408007 |○| Pyongyang | PRK | 2484000 || Bratislava | SVK | 448292 |○| Atlanta | USA | 416474 |○+------------+-------------+------------++ =Countryテーブルと結合するのはCityテーブルではなく、あくまでもSubというデータの集合(テンポラリテーブル)
  46. 46. 3章 サブクエリは「データ」の集合ではどうするか。SELECTCtr.Name CountryName,Sub.Name CityName,Sub.PopulationFROM Country CtrLEFT JOIN(SELECTCty.CountryCode,Cty.Name,Cty.PopulationFROM City CtyWHERE Cty.Population > 400000) SubON Ctr.Code = Sub.CountryCode;SELECTCtr.Name CountryName,Cty.Name CityName,Cty.PopulationFROM Country CtrLEFT JOIN City CtyON Ctr.Code = Cty.CountryCode;クエリSELECTCtr.Name CountryName,Cty.Name CityName,Cty.PopulationFROM Country CtrLEFT JOIN City CtyON Ctr.Code = Cty.CountryCodeAND Cty.Population > 400000;Ctr+------+---------------+| Code | Name |+------+---------------+| JPN | Japan || KOR | South Korea || SVK | Slovakia || USA | United States |+------+---------------+Cty+------------+-------------+------------+| Name | CountryCode | Population |+------------+-------------+------------+| Gifu | JPN | 408007 || Kwangmyong | KOR | 350914 || Pyongyang | PRK | 2484000 || Bratislava | SVK | 448292 || Atlanta | USA | 416474 |+------------+-------------+------------+Ctr LEFT JOIN Cty+---------------+------------+------------+| CountryName | CityName | Population |+---------------+------------+------------+| Japan | Gifu | 408007 || South Korea | Kwangmyong | 350914 || Slovakia | Bratislava | 448292 || United States | Atlanta | 416474 |+---------------+------------+------------++ =抽出イメージCity(都市テーブル)+-------------+------------+-----+| ColumnID | ColumnName | Key |+-------------+------------+-----+| ID | 都市ID | PRI || Name | 都市名 | || CountryCode | 国コード | MUL || District | 地区 | || Population | 都市人口 | MUL |+-------------+------------+-----+Country(国テーブル)+----------+---------------+-----+| ColumnID | ColumnName | Key |+----------+---------------+-----+| Code | 国コード | PRI || Name | 国名 | |==============省略================テーブルCty+------------+-------------+------------+| Name | CountryCode | Population |+------------+-------------+------------+| Gifu | JPN | 408007 || Kwangmyong | KOR | 350914 || Pyongyang | PRK | 2484000 || Bratislava | SVK | 448292 || Atlanta | USA | 416474 |+------------+-------------+------------+Ctr LEFT JOIN Cty+---------------+------------+------------+| CountryName | CityName | Population |+---------------+------------+------------+| Japan | Gifu | 408007 || South Korea | NULL | NULL || Slovakia | Bratislava | 448292 || United States | Atlanta | 416474 |+---------------+------------+------------+
  47. 47. +----+-------------+------------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+------+---------------+------+---------+------+------+-------------+| 1 | PRIMARY | Ctr | ALL | NULL | NULL | NULL | NULL | 241 | || 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 737 | || 2 | DERIVED | Cty | ALL | Population | NULL | NULL | NULL | 4051 | Using where |+----+-------------+------------+------+---------------+------+---------+------+------+-------------+3章 サブクエリは「データ」の集合修正後の実行計画は…+----+-------------+-------+------+------------------------+-------------+---------+----------------+------+-------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+------------------------+-------------+---------+----------------+------+-------+| 1 | SIMPLE | Ctr | ALL | NULL | NULL | NULL | NULL | 241 | || 1 | SIMPLE | Cty | ref | CountryCode,Population | CountryCode | 3 | world.Ctr.Code | 7 | |+----+-------------+-------+------+------------------------+-------------+---------+----------------+------+-------+EXPLAIN SELECTCtr.Name CountryName,Cty.Name CityName,Cty.PopulationFROM Country CtrLEFT JOIN City CtyON Ctr.Code = Cty.CountryCodeAND Cty.Population > 400000;クエリサブクエリを使わず直接テーブル同士をJOINしたことで、INDEXを利用したJOINが可能に!クエリチューニング前実行計画クエリチューニング後実行計画
  48. 48. 3章 サブクエリは「データ」の集合ちなみに・・・
  49. 49. 3章 サブクエリは「データ」の集合チューニング前のクエリの結合方式を内部結合にすると…+----+-------------+------------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+------+---------------+------+---------+------+------+-------------+| 1 | PRIMARY | Ctr | ALL | NULL | NULL | NULL | NULL | 241 | || 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 737 | || 2 | DERIVED | Cty | ALL | Population | NULL | NULL | NULL | 4051 | Using where |+----+-------------+------------+------+---------------+------+---------+------+------+-------------+EXPLAIN SELECTCtr.Name CountryName,Sub.Name CityName,Sub.PopulationFROM Country CtrLEFT JOIN (SELECTCty.CountryCode,Cty.Name,Cty.PopulationFROM City CtyWHERE Cty.Population > 400000) SubON Ctr.Code = Sub.CountryCode;EXPLAIN SELECTCtr.Name CountryName,Sub.Name CityName,Sub.PopulationFROM Country CtrINNER JOIN (SELECTCty.CountryCode,Cty.Name,Cty.PopulationFROM City CtyWHERE Cty.Population > 400000) SubON Ctr.Code = Sub.CountryCode;+----+-------------+------------+--------+---------------+---------+---------+-----------------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+--------+---------------+---------+---------+-----------------+------+-------------+| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 737 | || 1 | PRIMARY | Ctr | eq_ref | PRIMARY | PRIMARY | 3 | Sub.CountryCode | 1 | || 2 | DERIVED | Cty | ALL | Population | NULL | NULL | NULL | 4051 | Using where |+----+-------------+------------+--------+---------------+---------+---------+-----------------+------+-------------+INDEXが…使われている!?実行計画
  50. 50. 3章 サブクエリは「データ」の集合図で説明します+----+-------------+------------+--------+---------------+---------+---------+-----------------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+--------+---------------+---------+---------+-----------------+------+-------------+| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 737 | || 1 | PRIMARY | Ctr | eq_ref | PRIMARY | PRIMARY | 3 | Sub.CountryCode | 1 | || 2 | DERIVED | Cty | ALL | Population | NULL | NULL | NULL | 4051 | Using where |+----+-------------+------------+--------+---------------+---------+---------+-----------------+------+-------------+CtyPKINDEXderived2 Ctrderived2を基準にCtrとJOIN内部結合の場合、どちらを基準にしても良いため、オプティマイザが効率的と判断した集合を基準に結合する。サブクエリにて抽出した集合にはPKやINDEXの情報は含まれていない。PKPKINDEX
  51. 51. 3章 まとめサブクエリで抽出した集合にはINDEXは無い!サブクエリを結合する場合は、サブクエリ内で十分にデータを絞り込むか、別の抽出方法を検討すること。
  52. 52. 1章 対話的にクエリを作る2章 オプティマイザの判断3章 サブクエリは「データ」の集合4章 ソートとインデックス5章 インデックスは万能ではない6章 相関サブクエリは諸刃の剣休憩
  53. 53. 1章 対話的にクエリを作る2章 オプティマイザの判断3章 サブクエリは「データ」の集合4章 ソートとインデックス5章 インデックスは万能ではない6章 相関サブクエリは諸刃の剣休憩
  54. 54. 4章 ソートとインデックス全ての国名とその国が所有する都市数クエリSELECTCtr.Code CountryCode,Ctr.Name CountryName,COUNT(Cty.ID) CityCountFROM Country CtrLEFT JOIN City CtyON Ctr.Code = Cty.CountryCodeGROUP BY Ctr.Code,Ctr.Name;抽出仕様 抽出項目国コード国名都市数City(都市テーブル)+-------------+------------+-----+| ColumnID | ColumnName | Key |+-------------+------------+-----+| ID | 都市ID | PRI || Name | 都市名 | || CountryCode | 国コード | MUL || District | 地区 | || Population | 都市人口 | MUL |+-------------+------------+-----+Country(国テーブル)+----------+---------------+-----+| ColumnID | ColumnName | Key |+----------+---------------+-----+| Code | 国コード | PRI || Name | 国名 | |==============省略================テーブル抽出イメージCtr+------+---------------+| Code | Name |+------+---------------+| ATA | Antarctica || JPN | Japan || KOR | South Korea || USA | United States |+------+---------------+Cty+------+----------+-------------+| ID | Name | CountryCode |+------+----------+-------------+| 129 | Aruba | ABW || 1532 | Tokyo | JPN || 1534 | Osaka | JPN || 2331 | Seoul | KOR || 3793 | New York | USA |+------+----------+-------------+Ctr LEFT JOIN Cty+------+---------------+------+----------+-------------+| Code | Name | ID | Name | CountryCode |+------+---------------+------+----------+-------------+| ATA | Antarctica | NULL | NULL | NULL || JPN | Japan | 1532 | Tokyo | JPN || JPN | Japan | 1534 | Osaka | JPN || KOR | South Korea | 2331 | Seoul | KOR || USA | United States | 3793 | New York | USA |+------+---------------+------+----------+-------------++ =Ctr LEFT JOIN Cty GROUP BY Ctr.Column+-------------+---------------+-----------+| CountryCode | CountryName | CityCount |+-------------+---------------+-----------+| ATA | Antarctica | 0 || JPN | Japan | 2 || KOR | South Korea | 1 || USA | United States | 1 |+-------------+---------------+-----------+Cty+------+----------+-------------+| ID | Name | CountryCode |+------+----------+-------------+| 129 | Aruba | ABW || 1532 | Tokyo | JPN |○| 1534 | Osaka | JPN |○| 2331 | Seoul | KOR |○| 3793 | New York | USA |○+------+----------+-------------+
  55. 55. 4章 ソートとインデックスSELECTCtr.Code CountryCode,Ctr.Name CountryName,COUNT(Cty.ID) CityCountFROM Country CtrLEFT JOIN City CtyON Ctr.Code = Cty.CountryCodeGROUP BY Ctr.Code,Ctr.Name;+----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------+| 1 | SIMPLE | Ctr | ALL | NULL | NULL | NULL | NULL | 205 | Using temporary; Using filesort || 1 | SIMPLE | Cty | ref | CountryCode | CountryCode | 3 | world_big.Ctr.Code | 873 | Using index |+----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------+テーブルの結合キーに関してはINDEXが使われていますが、今回注目して頂きたいのは結合基準となるテーブルのExtraフィールドです。クエリの実行計画を確認してみます。実行計画+----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------+| 1 | SIMPLE | Ctr | ALL | NULL | NULL | NULL | NULL | 205 | Using temporary; Using filesort || 1 | SIMPLE | Cty | ref | CountryCode | CountryCode | 3 | world_big.Ctr.Code | 873 | Using index |+----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------++----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------+| 1 | SIMPLE | Ctr | ALL | NULL | NULL | NULL | NULL | 205 | Using temporary; Using filesort || 1 | SIMPLE | Cty | ref | CountryCode | CountryCode | 3 | world_big.Ctr.Code | 873 | Using index |+----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------+
  56. 56. 4章 ソートとインデックス図解するとこんな感じ。CtrCtr1Ctr2Ctr3Ctr4CtyCty1-1Cty3-1Cty2-2Cty2-1Cty4-1テンポラリテーブルLEFTJOINSORT出力Ctr1Ctr2Ctr3Ctr4Cty2-1Cty2-2Cty3-1Cty4-1Ctr1Ctr2Ctr3Ctr4Cty3-1Cty2-2Cty2-1Cty4-1Ctr+------+---------------+| Code | Name |+------+---------------+| USA | United States |4| JPN | Japan |2| ATA | Antarctica |1| KOR | South Korea |3+------+---------------+Cty+----------+-------------+| Name | CountryCode |+----------+-------------+| Osaka | JPN |2-2| Seoul | KOR |3-1| Aruba | ABW |1-1| New York | USA |4-1| Tokyo | JPN |2-1+----------+-------------+Ctr LEFT JOIN Cty GROUP BY Ctr.Columns+-------------+---------------+-----------+| CountryCode | CountryName | CityCount |+-------------+---------------+-----------+| ATA | Antarctica | 0 || JPN | Japan | 2 || KOR | South Korea | 1 || USA | United States | 1 |+-------------+---------------+-----------+テンポラリテーブルUsing temporary Using filesort
  57. 57. 4章 ソートとインデックスCtrCtr1Ctr2Ctr3Ctr4CtyCty1-1Cty3-1Cty2-2Cty2-1Cty4-1テンポラリテーブルLEFTJOINSORT出力Ctr1Ctr2Ctr3Ctr4Cty2-1Cty2-2Cty3-1Cty4-1Ctr1Ctr2Ctr3Ctr4Cty3-1Cty2-2Cty2-1Cty4-1Ctr+------+---------------+| Code | Name |+------+---------------+| USA | United States |4| JPN | Japan |2| ATA | Antarctica |1| KOR | South Korea |3+------+---------------+Cty+----------+-------------+| Name | CountryCode |+----------+-------------+| Tokyo | JPN |2-2| Seoul | KOR |3-1| Aruba | ABW |1-1| New York | USA |4-1| Osaka | JPN |2-1+----------+-------------+Ctr LEFT JOIN Cty GROUP BY Ctr_Column+-------------+---------------+-----------+| CountryCode | CountryName | CityCount |+-------------+---------------+-----------+| ATA | Antarctica | 0 || JPN | Japan | 2 || KOR | South Korea | 1 || USA | United States | 1 |+-------------+---------------+-----------+テンポラリテーブルUsing temporary Using filesort実はこいつら、やっつけられます。
  58. 58. 4章 ソートとインデックス対応方法の一つとして複合INDEXを用いる方法があります。SELECTCtr.Code CountryCode,Ctr.Name CountryName,COUNT(Cty.ID) CityCountFROM Country CtrLEFT JOIN City CtyON Ctr.Code = Cty.CountryCodeGROUP BY Ctr.Code,Ctr.Name;上記クエリのGROUP BY句にて使用する集計キーに対して、下記のコマンドで複合INDEXを貼ります。ALTER TABLE Country ADD INDEX mul_idx1(Code, Name);
  59. 59. 4章 ソートとインデックス複合INDEX対応後のクエリの実行計画を確認してみます。SELECTCtr.Code CountryCode,Ctr.Name CountryName,COUNT(Cty.ID) CityCountFROM Country CtrLEFT JOIN City CtyON Ctr.Code = Cty.CountryCodeGROUP BY Ctr.Code,Ctr.Name;+----+-------------+-------+-------+---------------+-------------+---------+--------------------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+-------+---------------+-------------+---------+--------------------+------+-------------+| 1 | SIMPLE | Ctr | index | NULL | mul_idx1 | 55 | NULL | 1 | Using index || 1 | SIMPLE | Cty | ref | CountryCode | CountryCode | 3 | world_big.Ctr.Code | 873 | Using index |+----+-------------+-------+-------+---------------+-------------+---------+--------------------+------+-------------++---------+----------+--------------+-------------+| Table | Key_name | Seq_in_index | Column_name |+---------+----------+--------------+-------------+| Country | PRIMARY | 1 | Code || Country | mul_idx1 | 1 | Code || Country | mul_idx1 | 2 | Name |+---------+----------+--------------+-------------+実行計画複合INDEXを貼ることによって、INDEXを用いたソートが可能に!
  60. 60. 4章 ソートとインデックスそもそも、ソートにINDEXを使うってどういうこと?
  61. 61. 4章 ソートとインデックスmul_idx1(Code, Name)のINDEX情報headAAA CCC333 333BBB222 222111 111CodeName① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨複合INDEXは下記図のようなINDEX情報となります。INDEXとして保持している情報はCode、Nameの順にソートされています。つまりINDEX順に行を取り出したその時、既にソートは終わっているのです。
  62. 62. 4章 ソートとインデックスCtrCtr1Ctr2Ctr3Ctr4CtyCty1-1Cty3-1Cty2-2Cty2-1Cty4-1LEFTJOINSORT出力Ctr1Ctr2Ctr3Ctr4Cty2-1Cty2-2Cty3-1Cty4-1Ctr1Ctr2Ctr3Ctr4Cty3-1Cty2-2Cty2-1Cty4-1Using temporary Using filesortつまりこういうこと。INDEX情報データ抽出CtrLEFT JOIN
  63. 63. 4章 ソートとインデックスCodeのみの単一INDEXではだめなの?
  64. 64. 4章 ソートとインデックスCodeのみのINDEX情報headAAA CCCBBBCode①単一INDEXは下記図のようなINDEX情報となります。② ③上の図だけでダメなのはわかりますね。インデックス順にデータを取得しても、Name列はソートされていない状態です。
  65. 65. 4章 ソートとインデックス複合INDEXの定義順を逆にするとどうなるの?
  66. 66. 4章 ソートとインデックスmul_idx1(Name, Code)のINDEX情報head1 3CCDCCC CCE2BBCBBB BBDAABAAA AACNameCode① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨複合INDEXの定義順を逆にしてみます。SELECTCtr.Code CountryCode,Ctr.Name CountryName,COUNT(Cty.ID) CityCountFROM Country CtrLEFT JOIN City CtyON Ctr.Code = Cty.CountryCodeGROUP BY Ctr.Code,Ctr.Name;クエリINDEX情報はName、Codeの順でソートされています。よって、INDEX順でデータを取得したとしても、Code、Name順にソートする必要が出てきます。
  67. 67. 4章 ソートとインデックス他にも色々あるので、この情報を基にいろいろ調べてみてください。
  68. 68. 4章 ソートとインデックス68実は・・・
  69. 69. 4章 ソートとインデックス当件、新たにINDEXを付けなくてもINDEX付与後以上のパフォーマンスにチューニングすることが可能です。次章で説明します。
  70. 70. 4章 まとめGROUP BY、ORDER BYでもインデックスの利用を意識すること!
  71. 71. 1章 対話的にクエリを作る2章 オプティマイザの判断3章 サブクエリは「データ」の集合4章 ソートとインデックス5章 インデックスは万能ではない6章 相関サブクエリは諸刃の剣休憩
  72. 72. 5章 インデックスは万能ではない4章で例に挙げたクエリの実際の実行時間は以下です。+----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------+| 1 | SIMPLE | Ctr | ALL | NULL | NULL | NULL | NULL | 205 | Using temporary; Using filesort || 1 | SIMPLE | Cty | ref | CountryCode | CountryCode | 3 | world_big.Ctr.Code | 873 | Using index |+----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------+複合INDEX付与前複合INDEX付与後+----+-------------+-------+-------+---------------+-------------+---------+--------------------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+-------+---------------+-------------+---------+--------------------+------+-------------+| 1 | SIMPLE | Ctr | index | NULL | mul_idx1 | 55 | NULL | 1 | Using index || 1 | SIMPLE | Cty | ref | CountryCode | CountryCode | 3 | world_big.Ctr.Code | 873 | Using index |+----+-------------+-------+-------+---------------+-------------+---------+--------------------+------+-------------+実行時間239 rows in set (0.26 sec)実行時間239 rows in set (0.59 sec)※データ量増
  73. 73. 5章 インデックスは万能ではないクエリ抽出仕様 抽出項目国コード国名都市数City(都市テーブル) (件数増幅:411979)+-------------+------------+-----+| ColumnID | ColumnName | Key |+-------------+------------+-----+| ID | 都市ID | PRI || Name | 都市名 | || CountryCode | 国コード | MUL || District | 地区 | || Population | 都市人口 | MUL |+-------------+------------+-----+Country(国テーブル)+----------+---------------+-----+| ColumnID | ColumnName | Key |+----------+---------------+-----+| Code | 国コード | PRI || Name | 国名 | |==============省略================テーブル抽出イメージCtr+------+---------------+| Code | Name |+------+---------------+| ATA | Antarctica || JPN | Japan || KOR | South Korea || USA | United States |+------+---------------+Cty+-------------+-----------+| CountryCode | CityCount |+-------------+-----------+| ABW | 2 || JPN | 2 || KOR | 1 || USA | 1 |+-------------+-----------+Ctr LEFT JOIN Sub GROUP BY Ctr_Column+-------------+---------------+-----------+| CountryCode | CountryName | CityCount |+-------------+---------------+-----------+| ATA | Antarctica | 0 || JPN | Japan | 2 || KOR | South Korea | 1 || USA | United States | 1 |+-------------+---------------+-----------++ =SELECTCtr.Code CountryCode,Ctr.Name CountryName,IFNULL(Sub.CityCount, 0) CityCountFROM Country CtrLEFT JOIN (SELECTCty.CountryCode,COUNT(*) CityCountFROM City CtyGROUP BY Cty.CountryCode) SubON Ctr.Code = Sub.CountryCode;Sub+-------------+-----------+| CountryCode | CityCount |+-------------+-----------+| ABW | 2 || JPN | 2 |○| KOR | 1 |○| USA | 1 |○+-------------+-----------+全ての国名とその国が所有する都市数
  74. 74. 5章 インデックスは万能ではないクエリの実行計画を確認してみます。SELECTCtr.Code CountryCode,Ctr.Name CountryName,IFNULL(Sub.CityCount, 0) CityCountFROM Country CtrLEFT JOIN (SELECTCty.CountryCode,COUNT(*) CityCountFROM City CtyGROUP BY Cty.CountryCode) SubON Ctr.Code = Sub.CountryCode;+----+-------------+------------+-------+---------------+-------------+---------+------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+-------+---------------+-------------+---------+------+--------+-------------+| 1 | PRIMARY | Ctr | ALL | NULL | NULL | NULL | NULL | 245 | || 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 232 | || 2 | DERIVED | Cty | index | NULL | CountryCode | 3 | NULL | 412116 | Using index |+----+-------------+------------+-------+---------------+-------------+---------+------+--------+-------------+サブクエリのGROUP BYにて単一INDEXを集計キーとして使用!※JOINがテーブルスキャンとなっているが負荷は軽微!実行計画
  75. 75. 5章 インデックスは万能ではない複合インデックス付与版クエリ作り直し版+----+-------------+------------+-------+---------------+-------------+---------+------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+-------+---------------+-------------+---------+------+--------+-------------+| 1 | PRIMARY | Ctr | ALL | NULL | NULL | NULL | NULL | 245 | || 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 232 | || 2 | DERIVED | Cty | index | NULL | CountryCode | 3 | NULL | 412116 | Using index |+----+-------------+------------+-------+---------------+-------------+---------+------+--------+-------------+実行時間239 rows in set (0.16 sec)実行時間+----+-------------+-------+-------+---------------+-------------+---------+--------------------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+-------+---------------+-------------+---------+--------------------+------+-------------+| 1 | SIMPLE | Ctr | index | NULL | mul_idx1 | 55 | NULL | 1 | Using index || 1 | SIMPLE | Cty | ref | CountryCode | CountryCode | 3 | world_big.Ctr.Code | 873 | Using index |+----+-------------+-------+-------+---------------+-------------+---------+--------------------+------+-------------+239 rows in set (0.26 sec)
  76. 76. 5章 まとめ安易にINDEXを貼る前に、様々なチューニングパターンを検討すること!
  77. 77. 1章 対話的にクエリを作る2章 オプティマイザの判断3章 サブクエリは「データ」の集合4章 ソートとインデックス5章 インデックスは万能ではない6章 相関サブクエリは諸刃の剣休憩
  78. 78. 6章 相関サブクエリは諸刃の剣各国の最大人口を誇る都市を抽出クエリSELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population = (SELECTMAX(Cty2.Population)FROM City Cty2WHERE Cty2.CountryCode = Cty1.CountryCode);City(都市テーブル) (件数増幅:411979)+-------------+------------+-----+| ColumnID | ColumnName | Key |+-------------+------------+-----+| ID | 都市ID | PRI || Name | 都市名 | || CountryCode | 国コード | MUL || District | 地区 | || Population | 都市人口 | MUL |+-------------+------------+-----+抽出項目国コード都市名都市人口抽出仕様テーブル
  79. 79. 6章 相関サブクエリは諸刃の剣クエリの実行計画を確認してみます。SELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population = (SELECTMAX(Cty2.Population)FROM City Cty2WHERE Cty2.CountryCode = Cty1.CountryCode);+----+--------------------+-------+------+---------------+-------------+---------+----------------------------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+--------------------+-------+------+---------------+-------------+---------+----------------------------+--------+-------------+| 1 | PRIMARY | Cty1 | ALL | NULL | NULL | NULL | NULL | 412116 | Using where || 2 | DEPENDENT SUBQUERY | Cty2 | ref | CountryCode | CountryCode | 3 | world_big.Cty1.CountryCode | 873 | |+----+--------------------+-------+------+---------------+-------------+---------+----------------------------+--------+-------------+相関サブクエリで構成されているため、select_typeにDEPENDENT SUBQUERYと表示されています。+----+--------------------+-------+------+---------------+-------------+---------+----------------------------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+--------------------+-------+------+---------------+-------------+---------+----------------------------+--------+-------------+| 1 | PRIMARY | Cty1 | ALL | NULL | NULL | NULL | NULL | 412116 | Using where || 2 | DEPENDENT SUBQUERY | Cty2 | ref | CountryCode | CountryCode | 3 | world_big.Cty1.CountryCode | 873 | |+----+--------------------+-------+------+---------------+-------------+---------+----------------------------+--------+-------------+外部クエリサブクエリ実行計画
  80. 80. 6章 相関サブクエリは諸刃の剣+-------------+--------------+------------+| CountryCode | Name | Population |+-------------+--------------+------------+① | JPN | Tokyo | 7980230 |② | JPN | Osaka | 2595674 |③ | JPN | Kamakura | 167661 |④ | CHN | Shanghai | 9696300 |⑤ | CHN | Kunming | 1829500 |⑥ | CHN | Dali | 136554 |⑦ | USA | New York | 8008278 |⑧ | USA | Houston | 1953631 |⑨ | USA | Hollywood | 139357 |+-------------+--------------+------------+データの内容相関サブクエリのイメージを図示すると以下のような感じです。⑨⑧Cty1②①⑥ ④⑤⑦ ③Cty2CountryCode=JPN CountryCode=CHN CountryCode=USA+-------------+--------------+------------+| CountryCode | Name | Population |+-------------+--------------+------------+① | JPN | Tokyo | 7980230 |④ | CHN | Shanghai | 9696300 |⑦ | USA | New York | 8008278 |+-------------+--------------+------------+クエリ実行結果SELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population = ();SELECTMAX(Cty2.Population)FROM City Cty2WHERE Cty2.CountryCode = Cty1.CountryCodeCty1.CountryCodeでCty2の集合を分割MAX(Cty2.Population)+-------------+--------------+------------+| CountryCode | Name | Population |+-------------+--------------+------------+① | JPN | Tokyo | 7980230 |② | JPN | Osaka | 2595674 |③ | JPN | Kamakura | 167661 |④ | CHN | Shanghai | 9696300 |⑤ | CHN | Kunming | 1829500 |⑥ | CHN | Dali | 136554 |⑦ | USA | New York | 8008278 |⑧ | USA | Houston | 1953631 |⑨ | USA | Hollywood | 139357 |+-------------+--------------+------------+
  81. 81. 6章 相関サブクエリは諸刃の剣テーブルアクセスイメージCty2Cty1row2row1row412115row412116Cty2Cty2Cty2+----+--------------------+-------+------+---------------+-------------+---------+----------------------------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+--------------------+-------+------+---------------+-------------+---------+----------------------------+--------+-------------+| 1 | PRIMARY | Cty1 | ALL | NULL | NULL | NULL | NULL | 412116 | Using where || 2 | DEPENDENT SUBQUERY | Cty2 | ref | CountryCode | CountryCode | 3 | world_big.Cty1.CountryCode | 873 | |+----+--------------------+-------+------+---------------+-------------+---------+----------------------------+--------+-------------+実行計画row1row2row412115row412116・・・873件スキャン873件スキャン873件スキャン873件スキャン359,777,268件スキャン!412116件スキャン
  82. 82. 6章 相関サブクエリは諸刃の剣82Cty2①SELECT Tokyo , JPN , 7980230 FROM City WHERE 7980230 = 7980230; ○②SELECT Osaka , JPN , 2595674 FROM City WHERE 2595674 = 7980230;③SELECT Kamakura , JPN , 167661 FROM City WHERE 167661 = 7980230;④SELECT Shanghai , CHN , 9696300 FROM City WHERE 9696300 = 9696300; ○⑤SELECT Kunming , CHN , 1829500 FROM City WHERE 1829500 = 9696300;⑥SELECT Dali , CHN , 136554 FROM City WHERE 136554 = 9696300;⑦SELECT New York , USA , 8008278 FROM City WHERE 8008278 = 8008278; ○⑧SELECT Houston , USA , 1953631 FROM City WHERE 1953631 = 8008278;⑨SELECT Hollywood , USA , 139357 FROM City WHERE 139357 = 8008278;⑨⑧Cty1②①⑥ ④⑤⑦ ③+-------------+--------------+------------+| CountryCode | Name | Population |+-------------+--------------+------------+① | JPN | Tokyo | 7980230 |② | JPN | Osaka | 2595674 |③ | JPN | Kamakura | 167661 |④ | CHN | Shanghai | 9696300 |⑤ | CHN | Kunming | 1829500 |⑥ | CHN | Dali | 136554 |⑦ | USA | New York | 8008278 |⑧ | USA | Houston | 1953631 |⑨ | USA | Hollywood | 139357 |+-------------+--------------+------------+データの内容クエリ展開図Cty1.CountryCodeMAX(Cty2.Population)SELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1WHERE Cty1.Population = ();SELECTMAX(Cty2.Population)FROM City Cty2WHERE Cty2.CountryCode = Cty1.CountryCode相関サブクエリの仕組みCty1のタプル数分、Cty2のクエリ発行を繰り返す
  83. 83. 6章 相関サブクエリは諸刃の剣実行結果 … 返ってきません。チューニングしましょう。
  84. 84. 6章 相関サブクエリは諸刃の剣各国の最大人口を誇る都市を抽出クエリCity(都市テーブル) (件数増幅:411979)+-------------+------------+-----+| ColumnID | ColumnName | Key |+-------------+------------+-----+| ID | 都市ID | PRI || Name | 都市名 | || CountryCode | 国コード | MUL || District | 地区 | || Population | 都市人口 | MUL |+-------------+------------+-----+抽出項目国コード都市名都市人口抽出仕様テーブルSELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1INNER JOIN (SELECTCty2.CountryCode,MAX(Cty2.Population) PopulationFROM City Cty2GROUP BY Cty2.CountryCode) SubON Cty1.CountryCode = Sub.CountryCodeAND Cty1.Population = Sub.Population;抽出イメージCty1+-------------+--------------+------------+| CountryCode | Name | Population |+-------------+--------------+------------+| JPN | Tokyo | 7980230 || JPN | Osaka | 2595674 || JPN | Kamakura | 167661 || CHN | Shanghai | 9696300 || CHN | Kunming | 1829500 || CHN | Dali | 136554 || USA | New York | 8008278 || USA | Houston | 1953631 || USA | Hollywood | 139357 |+-------------+--------------+------------+Sub (Cty2)+-------------+------------+| CountryCode | Population |+-------------+------------+| JPN | 7980230 || CHN | 9696300 || USA | 8008278 |+-------------+------------+Cty1 INNER JOIN Sub+-------------+--------------+------------+| CountryCode | Name | Population |+-------------+--------------+------------+| JPN | Tokyo | 7980230 || CHN | Shanghai | 9696300 || USA | New York | 8008278 |+-------------+--------------+------------++ =Cty1+-------------+--------------+------------+| CountryCode | Name | Population |+-------------+--------------+------------+| JPN | Tokyo | 7980230 || JPN | Osaka | 2595674 || JPN | Kamakura | 167661 || CHN | Shanghai | 9696300 || CHN | Kunming | 1829500 || CHN | Dali | 136554 || USA | New York | 8008278 || USA | Houston | 1953631 || USA | Hollywood | 139357 |+-------------+--------------+------------+
  85. 85. 6章 相関サブクエリは諸刃の剣クエリの実行計画を確認してみます。SELECTCty1.CountryCode,Cty1.Name,Cty1.PopulationFROM City Cty1INNER JOIN (SELECTCty2.CountryCode,MAX(Cty2.Population) PopulationFROM City Cty2GROUP BY Cty2.CountryCode) SubON Cty1.CountryCode = Sub.CountryCodeAND Cty1.Population = Sub.Population;+----+-------------+------------+-------+------------------------+-------------+---------+-------------------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+-------+------------------------+-------------+---------+-------------------+--------+-------------+| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 232 | || 1 | PRIMARY | Cty1 | ref | CountryCode,Population | Population | 4 | Sub.Population | 1 | Using where || 2 | DERIVED | Cty2 | index | NULL | CountryCode | 3 | NULL | 412116 | |+----+-------------+------------+-------+------------------------+-------------+---------+-------------------+--------+-------------+実行時間は以下の通りです。232 rows in set (0.76 sec)
  86. 86. 6章 相関サブクエリは諸刃の剣なんで速くなったの?
  87. 87. 6章 相関サブクエリは諸刃の剣テーブルアクセスイメージ+----+-------------+------------+-------+------------------------+-------------+---------+----------------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+-------+------------------------+-------------+---------+----------------+--------+-------------+| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 232 | || 1 | PRIMARY | Cty1 | ref | CountryCode,Population | Population | 4 | Sub.Population | 1 | Using where || 2 | DERIVED | Cty2 | index | NULL | CountryCode | 3 | NULL | 412116 | |+----+-------------+------------+-------+------------------------+-------------+---------+----------------+--------+-------------+チューニング後の実行計画Cty1 Cty2derived2412116件スキャン232件232 * 1件スキャン
  88. 88. 6章 まとめ相関サブクエリは便利かつ、それでしか実現できない抽出条件も存在する!しかし、ボトルネックの温床!
  89. 89. 1章 対話的にクエリを作る2章 オプティマイザの判断3章 サブクエリは「データ」の集合4章 ソートとインデックス5章 インデックスは万能ではない6章 相関サブクエリは諸刃の剣休憩最後に
  90. 90. Microadの職人たちを紹介します。※画像クリックでリンク先に遷移します。2013年6月時点のサイトトップ
  91. 91. 御清聴ありがとうございました

×