Это звучало как интересная проблема. Я создал таблицу тестового диапазона примерно так:
CREATE TABLE `test_ranges` (
`rangeid` int(11) NOT NULL,
`max` int(11) NOT NULL,
`min` int(11) NOT NULL,
PRIMARY KEY (`rangeid`),
KEY `idx_minmax` (`min`,`max`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Я вставил 50 000 строк в эту таблицу, каждая из которых имеет диапазон max-min = 10, например:
mysql> select * from test_ranges limit 2;
+---------+-----+-----+
| rangeid | max | min |
+---------+-----+-----+
| 1 | 15 | 5 |
| 2 | 20 | 10 |
+---------+-----+-----+
2 rows in set (0.00 sec)
Мой Perl-код для получения диапазонов, которые соответствуют списку целых чисел, - это создать временную таблицу для хранения целых чисел и попросить MySQL выполнить сопоставление для меня:
$DB->do_sql("CREATE TEMPORARY TABLE test_vals ( val int NOT NULL ) ENGINE=InnoDB");
for (12, 345, 394, 1450, 999, 9999, 99999, 999999 ) {
$DB->do_sql("INSERT INTO test_vals VALUES (?)", $_);
}
$answer = $DB->do_sql("SELECT DISTINCT * from test_vals, test_ranges WHERE val BETWEEN min AND max");
Это возвращает мне правильный список. В клиенте mysql это будет выглядеть так:
mysql> SELECT DISTINCT * from test_vals, test_ranges WHERE val BETWEEN min AND max;
+-------+---------+--------+-------+
| val | rangeid | max | min |
+-------+---------+--------+-------+
| 12 | 1 | 15 | 5 |
| 12 | 2 | 20 | 10 |
| 345 | 67 | 345 | 335 |
| 345 | 68 | 350 | 340 |
| 345 | 69 | 355 | 345 |
| 394 | 77 | 395 | 385 |
| 394 | 78 | 400 | 390 |
| 1450 | 288 | 1450 | 1440 |
| 1450 | 289 | 1455 | 1445 |
| 1450 | 290 | 1460 | 1450 |
| 999 | 198 | 1000 | 990 |
| 999 | 199 | 1005 | 995 |
| 9999 | 1998 | 10000 | 9990 |
| 9999 | 1999 | 10005 | 9995 |
| 99999 | 19998 | 100000 | 99990 |
| 99999 | 19999 | 100005 | 99995 |
+-------+---------+--------+-------+
16 rows in set (0.00 sec)
Или, просто для списка совпадающих значений:
mysql> SELECT DISTINCT val from test_vals, test_ranges WHERE val BETWEEN min AND max;
+-------+
| val |
+-------+
| 12 |
| 345 |
| 394 |
| 999 |
| 1450 |
| 9999 |
| 99999 |
+-------+
7 rows in set (0.00 sec)
MySQL (по крайней мере 5.0, на котором я сейчас) заявляет через EXPLAIN, что он не использует индекс для сравнения обычным способом. Тем не менее, он сообщает « Range проверен для каждой записи », что по существу означает, что он делает то, что вы думаете: обрабатывает значения из таблицы test_vals
как константы и ищет их таблица test_ranges
с индексом idx_minmax
.
mysql> explain SELECT DISTINCT * from test_vals, test_ranges WHERE val BETWEEN min AND max \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: test_vals
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 8
Extra: Using temporary
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: test_ranges
type: ALL
possible_keys: idx_minmax
key: NULL
key_len: NULL
ref: NULL
rows: 48519
Extra: Range checked for each record (index map: 0x2)
2 rows in set (0.00 sec)
Это довольно быстро, но я не знаю, сколько у вас будет больше строк, чем 8 и 50 КБ, с которыми я тестировал. Я полагаю, что создание такой временной таблицы было бы оптимальным решением, если у вас есть несколько небольших значений, которые вы ищете.