JOINs
2テーブルというクエリがあります。 lineitem
およびpart
、
select
sum(l_extendedprice* (1 - l_discount)) as revenue
from
lineitem force index for join (l_pk),
part
where
(
p_partkey = l_partkey
and p_brand = 'Brand#12'
and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG')
and l_quantity >= 1 and l_quantity <= 1 + 10
and p_size between 1 and 5
and l_shipmode in ('AIR', 'AIR REG')
and l_shipinstruct = 'DELIVER IN PERSON'
)
or
(
p_partkey = l_partkey
and p_brand = 'Brand#23'
and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK')
and l_quantity >= 10 and l_quantity <= 10 + 10
and p_size between 1 and 10
and l_shipmode in ('AIR', 'AIR REG')
and l_shipinstruct = 'DELIVER IN PERSON'
)
or
(
p_partkey = l_partkey
and p_brand = 'Brand#34'
and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG')
and l_quantity >= 20 and l_quantity <= 20 + 10
and p_size between 1 and 15
and l_shipmode in ('AIR', 'AIR REG')
and l_shipinstruct = 'DELIVER IN PERSON'
);
Lineitemにインデックスがあり、クエリでこのインデックスを結合に使用したいp_partkey = l_partkey
create index l_pk on tpch.lineitem(l_partkey);
MySQL explain
は以下を示します:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+--------+---------------+---------+---------+-------------------------+---------+-------------+
| 1 | SIMPLE | lineitem | ALL | l_pk | NULL | NULL | NULL | 5982534 | Using where |
| 1 | SIMPLE | part | eq_ref | PRIMARY | PRIMARY | 4 | tpch.lineitem.L_PARTKEY | 1 | Using where |
+----+-------------+----------+--------+---------------+---------+---------+-------------------------+---------+-------------+
なぜインデックスl_pk
使用されていない?
FORCE INDEX
を使用している場合でも、クエリオプティマイザーが期待どおりに引き継ぎました。
lineitem
テーブルのパスが、インデックスエントリ全体の5%を超えました。これが、クエリオプティマイザーがインデックスの使用を却下する原因になります。 Oracle、MSSQL、およびPostgreSQLは何も変わらないでしょう。
クエリをリファクタリングしようとする私の病気の試みは次のとおりです
select sum(l_extendedprice* (1 - l_discount)) as revenue
from
(
select p_partkey l_partkey,'S' contsize from part
where p_brand = 'Brand#12'
and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG')
and p_size between 1 and 5
union
select p_partkey,'M' contsize from part
where p_brand = 'Brand#23'
and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK')
and p_size between 1 and 10
union
select p_partkey,'L' contsize from part
where p_brand = 'Brand#34'
and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG')
and p_size between 1 and 15
) partkeys
left join listitem using (l_partkey)
where
l_shipmode in ('AIR', 'AIR REG') and
l_shipinstruct = 'DELIVER IN PERSON' and
IF(contsize='S',IF(l_quantity >= 1 and l_quantity <= 1 + 10,1,0),
IF(contsize='M',IF(l_quantity >= 10 and l_quantity <= 10 + 10,1,0),
IF(contsize='L',IF(l_quantity >= 20 and l_quantity <= 20 + 10,1,0),0)
)
)
;
テーブルにインデックスを付ける必要があるかもしれません
ALTER TABLE parts ADD INDEX brand_container_size_ndx
(p_brand,p_container,p_size,p_partkey);
ALTER TABLE listitem ADD INDEX partkey_shipinstruct_shipmode_quantity_ndx
(l_partkey,l_shipinstruct,l_shipmode ,l_quantity);
試してみて、高速か、それとも機能するかをお知らせください。
インデックスl_pk
は、テーブルの結合方法が原因で使用されていません。
インデックスを使用するには、そのインデックスで検索するものを用意する必要があります。
2つのテーブルを結合する場合、左側のテーブルに値があり、右側のテーブルの対応する行と一致させる必要があるため、右側のテーブルのインデックスを使用して一致する行を検索します。または、右のテーブルと左のテーブルの行を一致させる必要があるため、左のテーブルのインデックスを使用して一致する行を見つけます。
両方のテーブルでインデックスを使用して結合を実行するのではなく、どちらか一方を使用します。この場合、part
の主キーが結合に使用されています。
それで、次の質問は「なぜ?」です。
これに答えるために、クエリがサーバーに要求する内容を検討します。
サーバーによって実行されるこれらの2つのプロセスのどちらでも、同じ結果が生成されます。
サーバーに要求しています...
きみの possible_keys
それぞれに1つのインデックスのみが含まれています。これは、検索している列にインデックスがないことを示しています... p_brand、p_container、p_size、l_quantity、l_shipmode、l_shipinstruct ...
したがって、サーバーはいずれかのテーブルで全テーブルスキャンを実行する以外に選択肢はありません。これがlineitem
を選択しているのは、これが最も費用対効果の高いルートであると結論付けたからです。
実際の問題は、インデックスを作成する必要がある列を検索していることです。