19. 1章 対話的にクエリを作る
SELECT * FROM City WHERE Name = 'Tokyo'; City(都市テーブル) 件数:4079
+-------------+------------+-----+
| ColumnID | ColumnName | Key |
+-------------+------------+-----+
| ID | 都市ID | PRI |
| Name | 都市名 | MUL |
| CountryCode | 国コード | MUL |
| District | 地区 | |
| Population | 都市人口 | MUL |
+-------------+------------+-----+
row1863
City
NameカラムのINDEX情報
'Tokyo'
A - K
L - Z
A - D
E - G
H - I
L - O
P - R
S - Z
Peking
Pusan
Qazvin
Rotterdam
Roma
London
Liverpool
Mexico
New York
Okinawa
Sydney
Tokyo
Washington
Valera
Zelenograd
… … …
A - K
L - Z
L - O
P - R
S - Z
Sydney
Tokyo
Washington
Valera
Zelenograd
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 |
+-------------+------------+----------+------+-----+
サブクエリ
SELECT
Cty1.Name,
Cty1.CountryCode,
Cty1.Population
FROM City Cty1
WHERE Cty1.Population / 2 > (
SELECT
Cty2.Population
FROM City Cty2
WHERE Cty2.Name = 'Shenyang'
);
都市人口の半分が中国の瀋陽の人口よりも多い都市
瀋陽が中国の都市であることは明確であり、
抽出条件に国コードが追加されても問題はない。
SELECT
Cty1.Name,
Cty1.CountryCode,
Cty1.Population
FROM City Cty1
WHERE Cty1.Population / 2 > (
SELECT
Cty2.Population
FROM City Cty2
WHERE
AND Cty2.Name = 'Shenyang'
);
SELECT
Cty1.Name,
Cty1.CountryCode,
Cty1.Population
FROM City Cty1
WHERE Cty1.Population / 2 > (
SELECT
Cty2.Population
FROM City Cty2
WHERE Cty2.CountryCode = 'CHN'
AND Cty2.Name = 'Shenyang'
);
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 |
+----+-------------+-------+------+---------------+-------------+---------+-------+------+-------------+
SELECT
Cty1.CountryCode,
Cty1.Name,
Cty1.Population
FROM City Cty1
WHERE Cty1.Population / 2 > (
SELECT
Cty2.Population
FROM City Cty2
WHERE Cty2.CountryCode = 'CHN'
AND Cty2.Name = 'Shenyang'
);
クエリ
チューニング前の実行計画
サブクエリの検索条件にCountryCodeを指定した
ことで、INDEXを利用した検索が可能に!
チューニング後の実行計画
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 |
+-------------+------------+----------+------+-----+
クエリ
実行計画
SELECT
Cty1.CountryCode,
Cty1.Name,
Cty1.Population
FROM City Cty1
WHERE Cty1.Population / 2 > (
SELECT
Cty2.Population
FROM City Cty2
WHERE Cty2.CountryCode = 'CHN'
AND Cty2.Name = 'Shenyang'
);
あれ?!
INDEXは?!
SELECT
Cty1.CountryCode,
Cty1.Name,
Cty1.Population
FROM City Cty1
WHERE Cty1.Population / 2 > (
SELECT
Cty2.Population
FROM City Cty2
WHERE Cty2.CountryCode = 'CHN'
AND 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. 1章 対話的にクエリを作る
WHERE句内をよく見てください。
クエリ
SELECT
Cty1.CountryCode,
Cty1.Name,
Cty1.Population
FROM City Cty1
WHERE Cty1.Population / 2 > (
SELECT
Cty2.Population
FROM City Cty2
WHERE Cty2.CountryCode = 'CHN'
AND Cty2.Name = 'Shenyang'
);
SELECT
Cty1.CountryCode,
Cty1.Name,
Cty1.Population
FROM City Cty1
WHERE Cty1.Population / 2 > (
SELECT
Cty2.Population
FROM City Cty2
WHERE Cty2.CountryCode = 'CHN'
AND Cty2.Name = 'Shenyang'
);
索引列に対して演算処理を行っている場合、
オプティマイザはINDEXを使用することが出来ないのです。
25. 1章 対話的にクエリを作る
ではどうするか。
都市人口の半分が中国瀋陽の人口よりも多い都市
抽出仕様
SELECT
Cty1.CountryCode,
Cty1.Name,
Cty1.Population
FROM City Cty1
WHERE Cty1.Population / 2 > (
SELECT
Cty2.Population
FROM City Cty2
WHERE Cty2.CountryCode = 'CHN'
AND Cty2.Name = 'Shenyang'
);
SELECT
Cty1.CountryCode,
Cty1.Name,
Cty1.Population
FROM City Cty1
WHERE Cty1.Population > (
SELECT
Cty2.Population
FROM City Cty2
WHERE Cty2.CountryCode = 'CHN'
AND Cty2.Name = 'Shenyang'
) * 2;
= 都市人口が中国瀋陽の倍の人口よりも多い都市
抽出の仕様に影響なし
クエリ
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 |
+----+-------------+-------+-------+---------------+-------------+---------+------+------+-------------+
SELECT
Cty1.CountryCode,
Cty1.Name,
Cty1.Population
FROM City Cty1
WHERE Cty1.Population > (
SELECT
Cty2.Population
FROM City Cty2
WHERE Cty2.CountryCode = 'CHN'
AND Cty2.Name = 'Shenyang'
) * 2;
クエリ
クエリチューニング前実行計画
索引列の演算処理を、右辺に移すことで
INDEXを利用した範囲検索が可能に!
クエリチューニング後実行計画
29. 1章 対話的にクエリを作る
索引列を指定しても、オプティマイザがINDEXを
使用できないパターンは他にも存在します。
WHERE Index_Column IS NULL
NULL述語を使用
WHERE SUBSTRING(Index_Column, 1, 3) = 'abc'
SQL関数を使用
WHERE Index_Column <> 'abc'
否定形での条件指定
WHERE Index_Column = 'abc' OR Index_Column = 'def'
ORでの条件指定(INへの置き換えで対応可)
WHERE Index_Column LIKE '%abc%'
中間一致、後方一致でのLIKE述語を使用(前方一致は使用可能)
・
・
・
54. 4章 ソートとインデックス
全ての国名とその国が所有する都市数
クエリ
SELECT
Ctr.Code CountryCode,
Ctr.Name CountryName,
COUNT(Cty.ID) CityCount
FROM Country Ctr
LEFT JOIN City Cty
ON Ctr.Code = Cty.CountryCode
GROUP 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. 4章 ソートとインデックス
SELECT
Ctr.Code CountryCode,
Ctr.Name CountryName,
COUNT(Cty.ID) CityCount
FROM Country Ctr
LEFT JOIN City Cty
ON Ctr.Code = Cty.CountryCode
GROUP 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 |
+----+-------------+-------+------+---------------+-------------+---------+--------------------+------+---------------------------------+
66. 4章 ソートとインデックス
mul_idx1(Name, Code)のINDEX情報
head
1 3
CCDCCC CCE
2
BBCBBB BBDAABAAA AAC
Name
Code
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨
複合INDEXの定義順を逆にしてみます。
SELECT
Ctr.Code CountryCode,
Ctr.Name CountryName,
COUNT(Cty.ID) CityCount
FROM Country Ctr
LEFT JOIN City Cty
ON Ctr.Code = Cty.CountryCode
GROUP BY Ctr.Code,
Ctr.Name;
クエリ
INDEX情報はName、Codeの順でソートされています。
よって、INDEX順でデータを取得したとしても、
Code、Name順にソートする必要が出てきます。
82. 6章 相関サブクエリは諸刃の剣
82
Cty2
①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.CountryCode
MAX(Cty2.Population)
SELECT
Cty1.CountryCode,
Cty1.Name,
Cty1.Population
FROM City Cty1
WHERE Cty1.Population = ();
SELECT
MAX(Cty2.Population)
FROM City Cty2
WHERE Cty2.CountryCode = Cty1.CountryCode
相関サブクエリの仕組み
Cty1のタプル数分、
Cty2のクエリ発行を
繰り返す