テーブルスキャン
mysql56> EXPLAINSELECT Name, Continent, Population
-> FROM Country
-> WHERE Continent = 'Asia'
-> ORDER BY Population LIMIT 5;
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
| 1 | SIMPLE | Country | ALL | NULL | NULL | NULL | NULL | 239 | Using where; Using filesort |
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
1 row in set (0.00 sec)
* 1行ずつデータをフェッチして
* WHERE句のカラムで評価し
* ソートバッファに詰め込み
* クイックソートして
* 先頭から5件取り出す
31.
テーブルスキャン
for (my$rownum= 0; $rownum < scalar(@$country_table); $rownum++)
{
my $row= $country_table->[$rownum];
$evaluted++;
if ($row->{continent} eq "Asia")
{
$sorted++;
push(@{$sort_buffer->{$row->{population}}}, $rownum);
}
}
my $sorted_buffer= filesort_single_column($sort_buffer);
foreach my $rownum (@$sorted_buffer)
{
my $row= $country_table->[$rownum];
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last;}
}
scan_where_scan_orderby.pl
32.
テーブルスキャン
for (my$rownum= 0; $rownum < scalar(@$country_table); $rownum++)
{
my $row= $country_table->[$rownum];
$evaluted++;
if ($row->{continent} eq "Asia")
{
$sorted++;
push(@{$sort_buffer->{$row->{population}}}, $rownum);
}
}
my $sorted_buffer= filesort_single_column($sort_buffer);
foreach my $rownum (@$sorted_buffer)
{
my $row= $country_table->[$rownum];
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last;}
}
1行ずつフェッチして
scan_where_scan_orderby.pl
33.
テーブルスキャン
for (my$rownum= 0; $rownum < scalar(@$country_table); $rownum++)
{
my $row= $country_table->[$rownum];
$evaluted++;
if ($row->{continent} eq "Asia")
{
$sorted++;
push(@{$sort_buffer->{$row->{population}}}, $rownum);
}
}
my $sorted_buffer= filesort_single_column($sort_buffer);
foreach my $rownum (@$sorted_buffer)
{
my $row= $country_table->[$rownum];
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last;}
}
WHERE句で評価
scan_where_scan_orderby.pl
34.
テーブルスキャン
for (my$rownum= 0; $rownum < scalar(@$country_table); $rownum++)
{
my $row= $country_table->[$rownum];
$evaluted++;
if ($row->{continent} eq "Asia")
{
$sorted++;
push(@{$sort_buffer->{$row->{population}}}, $rownum);
}
}
my $sorted_buffer= filesort_single_column($sort_buffer);
foreach my $rownum (@$sorted_buffer)
{
my $row= $country_table->[$rownum];
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last;}
}
マッチしたら
ソートバッファに詰める
scan_where_scan_orderby.pl
35.
テーブルスキャン
for (my$rownum= 0; $rownum < scalar(@$country_table); $rownum++)
{
my $row= $country_table->[$rownum];
$evaluted++;
if ($row->{continent} eq "Asia")
{
$sorted++;
push(@{$sort_buffer->{$row->{population}}}, $rownum);
}
}
my $sorted_buffer= filesort_single_column($sort_buffer);
foreach my $rownum (@$sorted_buffer)
{
my $row= $country_table->[$rownum];
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last;}
}
ソートバッファの中身を
クイックソート
scan_where_scan_orderby.pl
36.
テーブルスキャン
for (my$rownum= 0; $rownum < scalar(@$country_table); $rownum++)
{
my $row= $country_table->[$rownum];
$evaluted++;
if ($row->{continent} eq "Asia")
{
$sorted++;
push(@{$sort_buffer->{$row->{population}}}, $rownum);
}
}
my $sorted_buffer= filesort_single_column($sort_buffer);
foreach my $rownum (@$sorted_buffer)
{
my $row= $country_table->[$rownum];
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last;}
}
先頭の5件を出力したら
そこでループを抜ける
scan_where_scan_orderby.pl
37.
テーブルスキャン
$ ./scan_where_scan_orderby.pl
Maldives Asia 286000
Brunei Asia 328000
Macao Asia 473000
Qatar Asia 599000
Bahrain Asia 617000
Total rows evaluted are 239, sorted are 51.
scan_where_scan_orderby.pl
5行の出力に対して、
239回のWHERE評価と
51行のファイルソート
WHERE狙いのキー
mysql56> ALTERTABLE Country ADD KEY index_continent(continent);
Query OK, 0 rows affected (0.07 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql56> EXPLAIN SELECT Name, Continent, Population
-> FROM Country
-> WHERE Continent = 'Asia'
-> ORDER BY Population LIMIT 5;
+----+-------------+---------+------+-----------------+-----------------+---------+-------+------
+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
|
+----+-------------+---------+------+-----------------+-----------------+---------+-------+------
+----------------------------------------------------+
| 1 | SIMPLE | Country | ref | index_continent | index_continent | 1 | const | 51 | Using index condition; Using
where; Using filesort |
+----+-------------+---------+------+-----------------+-----------------+---------+-------+------
+----------------------------------------------------+
1 row in set (0.00 sec)
* WHERE句のレンジをインデックスから取り出し
* ソートバッファに詰め込み
* クイックソートして
* 先頭から5件取り出す
48.
WHERE狙いのキー
my $index_num= $country_index->{map}->{Asia};
my $rownum_array= $country_index->{index}->[$index_num];
foreach my $rownum (@$rownum_array)
{
my $row= $country_table->[$rownum];
$sorted++;
push(@{$sort_buffer->{$row->{population}}}, $rownum);
}
my $sorted_buffer= filesort_single_column($sort_buffer);
foreach my $rownum (@$sorted_buffer)
{
my $row= $country_table->[$rownum];
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last;}
}
indexed_where_scan_orderby.pl
49.
WHERE狙いのキー
my $index_num= $country_index->{map}->{Asia};
my $rownum_array= $country_index->{index}->[$index_num];
foreach my $rownum (@$rownum_array)
{
my $row= $country_table->[$rownum];
$sorted++;
push(@{$sort_buffer->{$row->{population}}}, $rownum);
}
my $sorted_buffer= filesort_single_column($sort_buffer);
foreach my $rownum (@$sorted_buffer)
{
my $row= $country_table->[$rownum];
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last;}
}
WHERE句のレンジを
インデックスから取り出す
indexed_where_scan_orderby.pl
50.
WHERE狙いのキー
my $index_num= $country_index->{map}->{Asia};
my $rownum_array= $country_index->{index}->[$index_num];
foreach my $rownum (@$rownum_array)
{
my $row= $country_table->[$rownum];
$sorted++;
push(@{$sort_buffer->{$row->{population}}}, $rownum);
}
my $sorted_buffer= filesort_single_column($sort_buffer);
foreach my $rownum (@$sorted_buffer)
{
my $row= $country_table->[$rownum];
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last;}
}
取り出した行を全部
ソートバッファに詰めて
indexed_where_scan_orderby.pl
51.
WHERE狙いのキー
my $index_num= $country_index->{map}->{Asia};
my $rownum_array= $country_index->{index}->[$index_num];
foreach my $rownum (@$rownum_array)
{
my $row= $country_table->[$rownum];
$sorted++;
push(@{$sort_buffer->{$row->{population}}}, $rownum);
}
my $sorted_buffer= filesort_single_column($sort_buffer);
foreach my $rownum (@$sorted_buffer)
{
my $row= $country_table->[$rownum];
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last;}
}
クイックソートして
indexed_where_scan_orderby.pl
52.
WHERE狙いのキー
my $index_num= $country_index->{map}->{Asia};
my $rownum_array= $country_index->{index}->[$index_num];
foreach my $rownum (@$rownum_array)
{
my $row= $country_table->[$rownum];
$sorted++;
push(@{$sort_buffer->{$row->{population}}}, $rownum);
}
my $sorted_buffer= filesort_single_column($sort_buffer);
foreach my $rownum (@$sorted_buffer)
{
my $row= $country_table->[$rownum];
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last;}
}
先頭の5件を出力したら
ループを抜ける
indexed_where_scan_orderby.pl
53.
WHERE狙いのキー
$ ./indexed_where_scan_orderby.pl
Maldives Asia 286000
Brunei Asia 328000
Macao Asia 473000
Qatar Asia 599000
Bahrain Asia 617000
Total rows evaluted are 1, sorted are 51.
WHEREの評価は1回で済むけど
indexed_where_scan_orderby.pl
51行をファイルソート
ORDER BY狙いのキー
mysql56>ALTER TABLE Country ADD KEY index_population(population);
Query OK, 0 rows affected (0.07 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql56> EXPLAIN SELECT Name, Continent, Population
-> FROM Country
-> WHERE Continent = 'Asia'
-> ORDER BY Population LIMIT 5;
+----+-------------+---------+-------+---------------+------------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+------------------+---------+------+------+-------------+
| 1 | SIMPLE | Country | index | NULL | index_population | 4 | NULL | 5 | Using where |
+----+-------------+---------+-------+---------------+------------------+---------+------+------+-------------+
1 row in set (0.00 sec)
* インデックスに沿って行を取り出し
* WHERE句にマッチするか判定して
* 5件データが揃ったらループから抜ける
57.
ORDER BY狙いのキー
my$cardinality= scalar(keys(%{$index->{map}}));
LOOP: for (my $index_num= 0;
$index_num < $cardinality;
$index_num++)
{
my $rownum_array= $index->{index}->[$index_num];
foreach my $rownum (@$rownum_array)
{
my $row= $country_table->[$rownum];
$evaluted++;
if ($row->{continent} eq "Asia")
{
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last LOOP;}
}
}
}
scan_where_indexed_orderby.pl
58.
ORDER BY狙いのキー
my$cardinality= scalar(keys(%{$index->{map}}));
LOOP: for (my $index_num= 0;
$index_num < $cardinality;
$index_num++)
{
my $rownum_array= $index->{index}->[$index_num];
foreach my $rownum (@$rownum_array)
{
my $row= $country_table->[$rownum];
$evaluted++;
if ($row->{continent} eq "Asia")
{
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last LOOP;}
}
}
}
インデックスに沿って
行を取り出して
scan_where_indexed_orderby.pl
59.
ORDER BY狙いのキー
my$cardinality= scalar(keys(%{$index->{map}}));
LOOP: for (my $index_num= 0;
$index_num < $cardinality;
$index_num++)
{
my $rownum_array= $index->{index}->[$index_num];
foreach my $rownum (@$rownum_array)
{
my $row= $country_table->[$rownum];
$evaluted++;
if ($row->{continent} eq "Asia")
{
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last LOOP;}
}
}
}
WHERE句にマッチするか
判定して
scan_where_indexed_orderby.pl
60.
ORDER BY狙いのキー
my$cardinality= scalar(keys(%{$index->{map}}));
LOOP: for (my $index_num= 0;
$index_num < $cardinality;
$index_num++)
{
my $rownum_array= $index->{index}->[$index_num];
foreach my $rownum (@$rownum_array)
{
my $row= $country_table->[$rownum];
$evaluted++;
if ($row->{continent} eq "Asia")
{
printf("%st%st%dn",
$row->{name},
$row->{continent},
$row->{population});
if (++$count >= 5)
{last LOOP;}
}
}
}
先頭の5件を出力したら
そこでループを抜ける
scan_where_indexed_orderby.pl
61.
ORDER BY狙いのキー
$./scan_where_indexed_orderby.pl
Maldives Asia 286000
Brunei Asia 328000
Macao Asia 473000
Qatar Asia 599000
Bahrain Asia 617000
Total rows evaluted are 79, sorted are 0.
ソートのオーバーヘッドはないものの、
scan_where_indexed_orderby.pl
79行をWHERE句評価
スキャンジョイン
mysql56> EXPLAINSELECT Name, Language, Population, Percentage
-> FROM Country INNER JOIN CountryLanguage ON Country.Code= CountryLanguage.CountryCode
-> WHERE Country.continent = 'Asia'
-> ORDER BY Percentage LIMIT 5;
+----+-------------+-----------------+------+---------------+------+---------+------+------
+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
|
+----+-------------+-----------------+------+---------------+------+---------+------+------
+----------------------------------------------------+
| 1 | SIMPLE | CountryLanguage | ALL | NULL | NULL | NULL | NULL | 984 | Using temporary; Using filesort
|
| 1 | SIMPLE | Country | ALL | NULL | NULL | NULL | NULL | 239 | Using where; Using join buffer
(Block Nested Loop) |
+----+-------------+-----------------+------+---------------+------+---------+------+------
+----------------------------------------------------+
2 rows in set (0.00 sec)
* 外側のテーブルから1行データをフェッチして
* WHEREとONのカラムで内側のテーブルを評価して
ソートバッファに詰め込み
* 外側のテーブルから次の1行をフェッチして…を繰り返し
* クイックソートして先頭から5件取り出す
79.
スキャンジョイン
for (my$language_rownum= 0;
$language_rownum < scalar(@$language_table);
$language_rownum++)
{
my $language_row= $language_table->[$language_rownum];
for (my $country_rownum= 0;
$country_rownum < scalar(@$country_table);
$country_rownum++)
{
my $country_row= $country_table->[$country_rownum];
$evaluted++;
if ($language_row->{countrycode} eq $country_row->{code} &&
$country_row->{continent} eq "Asia")
{
$sorted++;
push(@{$sort_buffer->{$language_row->{percentage}}},
[$language_rownum, $country_rownum]);
}
}
}
ファイルソート以降は略
scan_join_scan_where_scan_orderby.pl
80.
スキャンジョイン
for (my$language_rownum= 0;
$language_rownum < scalar(@$language_table);
$language_rownum++)
{
my $language_row= $language_table->[$language_rownum];
for (my $country_rownum= 0;
$country_rownum < scalar(@$country_table);
$country_rownum++)
{
my $country_row= $country_table->[$country_rownum];
$evaluted++;
if ($language_row->{countrycode} eq $country_row->{code} &&
$country_row->{continent} eq "Asia")
{
$sorted++;
push(@{$sort_buffer->{$language_row->{percentage}}},
[$language_rownum, $country_rownum]);
}
}
}
ファイルソート以降は略
外側のテーブルから
1行ずつフェッチ
scan_join_scan_where_scan_orderby.pl
81.
スキャンジョイン
for (my$language_rownum= 0;
$language_rownum < scalar(@$language_table);
$language_rownum++)
{
my $language_row= $language_table->[$language_rownum];
for (my $country_rownum= 0;
$country_rownum < scalar(@$country_table);
$country_rownum++)
{
my $country_row= $country_table->[$country_rownum];
$evaluted++;
if ($language_row->{countrycode} eq $country_row->{code} &&
$country_row->{continent} eq "Asia")
{
$sorted++;
push(@{$sort_buffer->{$language_row->{percentage}}},
[$language_rownum, $country_rownum]);
}
}
}
ファイルソート以降は略
内側のテーブルでも
1行ずつフェッチして
scan_join_scan_where_scan_orderby.pl
82.
スキャンジョイン
for (my$language_rownum= 0;
$language_rownum < scalar(@$language_table);
$language_rownum++)
{
my $language_row= $language_table->[$language_rownum];
for (my $country_rownum= 0;
$country_rownum < scalar(@$country_table);
$country_rownum++)
{
my $country_row= $country_table->[$country_rownum];
$evaluted++;
if ($language_row->{countrycode} eq $country_row->{code} &&
$country_row->{continent} eq "Asia")
{
$sorted++;
push(@{$sort_buffer->{$language_row->{percentage}}},
[$language_rownum, $country_rownum]);
}
}
}
ファイルソート以降は略
評価&ソートバッファ
scan_join_scan_where_scan_orderby.pl
83.
スキャンジョイン
$ ./scan_join_scan_where_scan_orderby.pl
United Arab Emirates Hindi 2441000 0.000000
Bahrain English 617000 0.000000
Japan Ainu 126714000 0.000000
Kuwait English 1972000 0.000000
Lebanon French 3282000 0.000000
Total rows evaluted are 235176, sorted are 239.
評価する行の数が倍倍ゲェム
scan_join_scan_where_scan_orderby.pl
直感の赴くまま
SELECT Name,Language, Population, Percentage
FROM Country INNER JOIN CountryLanguage ON Country.Code= CountryLanguage.CountryCode
WHERE Country.continent = 'Asia'
ORDER BY CountryLanguage.Percentage LIMIT 5;
mysql56> ALTER TABLE Country ADD KEY index_continent(continent);
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql56> ALTER TABLE CountryLanguage
-> ADD KEY index_countrycode_percentage(countrycode, percentage);
Query OK, 0 rows affected (0.11 sec)
Records: 0 Duplicates: 0 Warnings: 0
これは残念ながらWHERE狙いのキーになる。
わかりますん。
86.
JOIN de WHERE狙いのキー
mysql56> EXPLAIN SELECT Name, Language, Population, Percentage
-> FROM Country INNER JOIN CountryLanguage ON Country.Code= CountryLanguage.CountryCode
-> WHERE Country.continent = 'Asia'
-> ORDER BY Percentage LIMIT 5;
+----+-------------+-----------------+------+------------------------------+------------------------------+---------
+--------------------+------+--------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref
| rows | Extra |
+----+-------------+-----------------+------+------------------------------+------------------------------+---------
+--------------------+------+--------------------------------------------------------+
| 1 | SIMPLE | Country | ref | index_continent | index_continent | 33 | const
| 51 | Using index condition; Using temporary; Using filesort |
| 1 | SIMPLE | CountryLanguage | ref | index_countrycode_percentage | index_countrycode_percentage | 3 |
world.Country.Code | 2 | NULL |
+----+-------------+-----------------+------+------------------------------+------------------------------+---------
+--------------------+------+--------------------------------------------------------+
2 rows in set (0.00 sec)
* 外側のテーブルをインデックスで刈り込んで
* 内側のテーブルもインデックスで刈り込んで
* ソートバッファに詰め込み
* ループして
* クイックソートして先頭から5件取り出す
87.
JOIN de WHERE狙いのキー
my $country_index_num = $country_index->{map}->{Asia};
my $country_rownum_array= $country_index->{index}->[$country_index_num];
foreach my $country_rownum (@$country_rownum_array)
{
my $country_row= $country_table->[$country_rownum];
$evaluted++;
my $language_index_num = $language_index->{map}->{$country_row->{code}}->{self};
my $language_index_range= $language_index->{index}->[$language_index_num];
foreach my $language_rownum_array (@$language_index_range)
{
foreach my $language_rownum (@$language_rownum_array)
{
my $language_row= $language_table->[$language_rownum];
$sorted++;
push(@{$sort_buffer->{$language_row->{percentage}}},
[$country_rownum, $language_rownum]);
}
}
}
ファイルソート以降は略
indexed_join_indexed_where_scan_orderby.pl
88.
JOIN de WHERE狙いのキー
my $country_index_num = $country_index->{map}->{Asia};
my $country_rownum_array= $country_index->{index}->[$country_index_num];
foreach my $country_rownum (@$country_rownum_array)
{
my $country_row= $country_table->[$country_rownum];
$evaluted++;
my $language_index_num = $language_index->{map}->{$country_row->{code}}->{self};
my $language_index_range= $language_index->{index}->[$language_index_num];
foreach my $language_rownum_array (@$language_index_range)
{
foreach my $language_rownum (@$language_rownum_array)
{
my $language_row= $language_table->[$language_rownum];
$sorted++;
push(@{$sort_buffer->{$language_row->{percentage}}},
[$country_rownum, $language_rownum]);
}
}
}
ファイルソート以降は略
外側のテーブルを
WHEREで刈り込んで
indexed_join_indexed_where_scan_orderby.pl
89.
JOIN de WHERE狙いのキーその1
my $country_index_num = $country_index->{map}->{Asia};
my $country_rownum_array= $country_index->{index}->[$country_index_num];
foreach my $country_rownum (@$country_rownum_array)
{
my $country_row= $country_table->[$country_rownum];
$evaluted++;
my $language_index_num = $language_index->{map}->{$country_row->{code}}->{self};
my $language_index_range= $language_index->{index}->[$language_index_num];
foreach my $language_rownum_array (@$language_index_range)
{
foreach my $language_rownum (@$language_rownum_array)
{
my $language_row= $language_table->[$language_rownum];
$sorted++;
push(@{$sort_buffer->{$language_row->{percentage}}},
[$country_rownum, $language_rownum]);
}
}
}
ファイルソート以降は略
刈り込んだ外側を
1行ずつキーにして
内側テーブルを刈り込む
indexed_join_indexed_where_scan_orderby.pl
90.
JOIN de WHERE狙いのキー
my $country_index_num = $country_index->{map}->{Asia};
my $country_rownum_array= $country_index->{index}->[$country_index_num];
foreach my $country_rownum (@$country_rownum_array)
{
my $country_row= $country_table->[$country_rownum];
$evaluted++;
my $language_index_num = $language_index->{map}->{$country_row->{code}}->{self};
my $language_index_range= $language_index->{index}->[$language_index_num];
foreach my $language_rownum_array (@$language_index_range)
{
foreach my $language_rownum (@$language_rownum_array)
{
my $language_row= $language_table->[$language_rownum];
$sorted++;
push(@{$sort_buffer->{$language_row->{percentage}}},
[$country_rownum, $language_rownum]);
}
}
}
ファイルソート以降は略
内側まで刈り込んだら
ソートバッファに詰める
indexed_join_indexed_where_scan_orderby.pl
91.
JOIN de WHERE狙いのキー
$ ./indexed_join_indexed_where_scan_orderby.pl
United Arab Emirates Hindi 2441000 0.000000
Bahrain English 617000 0.000000
Japan Ainu 126714000 0.000000
Kuwait English 1972000 0.000000
Lebanon French 3282000 0.000000
Total rows evaluted are 52, sorted are 239.
スキャンジョインよりよっぽどいいけど
そんなに効率が良いわけではなさそう
indexed_join_indexed_where_scan_orderby.pl