table1
の1つの列の値がtable2
の2つの列の値の間にあるという基準で2つのテーブルを結合しようとしています。
CREATE TABLE `values`(
`id` INT,
`name` VARCHAR(50),
`num_addr` BIGINT UNSIGNED
);
CREATE TABLE `ranges`(
`id` INT,
`range_name` VARCHAR(50),
`range_start` BIGINT UNSIGNED,
`range_end` BIGINT UNSIGNED,
INDEX `idx_start` (`range_start` ASC),
INDEX `idx_end` (`range_end` ASC),
INDEX `idx_range` (`range_start` ASC, `range_end` ASC)
);
クエリ:
SELECT
`v`.`name`,
`v`.`num_addr`,
`r`.`range_name`
FROM
`values` `v`
LEFT JOIN `ranges` `r` ON `v`.`num_addr` BETWEEN `range_start` AND `range_end`
クエリのEXPLAIN EXTENDED
は、インデックスが使用されていないことを示し、「各レコードの範囲がチェックされました(インデックスマップ:0x7)」という情報が含まれます。範囲テーブルには500,000を超える行があり、クエリは時間依存であるため、これはパフォーマンスの問題です。
# id, select_type, table, type, possible_keys, key, key_len, ref, rows, filtered, Extra
1, SIMPLE, r, ALL, idx_start,idx_end,idx_range, , , , 1, 100.00, Range checked for each record (index map: 0x7)
FORCE INDEX ON JOIN
は、オプティマイザが推奨インデックスのみを認識し、使用しないため、実際には事態を悪化させます。
そのような結合でインデックスを使用する方法はありますか?
その他の注意事項:
BETWEEN
をvalue >= range_start AND value <= range_end
に変更しても、実行計画は変更されません。idx_start
およびidx_end
インデックスを削除しても、状況は改善されません。num_addr
に追加しても、結合の実行には影響しません。(range_start, range_end)
はオーバーラップできます。num_addr
があります。ranges
テーブルには、レコード('UK', 44000000000000, 44999999999999)
と別の('UK Vodafone', 44700000000000, 44799999999999)
があります。範囲の重複は、最適化が特に難しい問題です。ただし、これは大きなパフォーマンスを提供する手法ですが、スキーマを大幅に変更する必要があります。
別のテーブルを追加します。 Prefixes
としましょう。次の2つの列があります。
prefix DECIMAL(4,0) NOT NULL,
range_id INT, -- for JOINing to `ranges`
PRIMARY KEY(prefix)
次に、数値4432109...
を探すには、まず4432
range.id`の値でPrefixes. This will lead to one or more
を調べて、既存のテーブルをチェックインします。
例では、Prefixes
には「UK」の100エントリと「UK Vodafone」の10エントリがあることに注意してください。これは、Prefixes
を維持するための追加のコードを意味します。
これのバリエーションは、残りのranges
列を新しいテーブルに移動し、テーブルranges
を削除することです。 (これを行うかどうかは、列の数と性質、コードの面倒、「プレフィックス」のサイズ、テーブルのサイズなどに依存します。)