在数据库表中查找范围内的值

时间:2012-01-18 01:14:23

标签: mysql

我需要等同于this的SQL。

我有一张这样的表

ID MN MX
-- -- --
A  0  3
B  4  6
C  7  9

给出一个数字,比如说5,我想找到MN和MX包含该数字的行的ID,在这种情况下是B。

显然,

SELECT ID FROM T WHERE ? BETWEEN MN AND MX

会这样做,但我有900万行,我想让它尽可能快地运行。特别是,我知道只能只有一个匹配的行,我现在MN-MX范围完全覆盖了空间,依此类推。由于对可能的答案有所有这些限制,我应该做一些优化。不应该吗?

到目前为止,我所有人都在使用以下

索引MN
SELECT ID FROM T WHERE ? BETWEEN MN AND MX ORDER BY MN LIMIT 1

但这很弱。

2 个答案:

答案 0 :(得分:3)

如果你有一个跨越MN和MX的索引它应该非常快,即使有9M行。

alter table T add index mn_mx (mn, mx);

修改

我刚尝试了一个带有1M行表的测试

mysql> select count(*) from T;
+----------+
| count(*) |
+----------+
|  1000001 |
+----------+
1 row in set (0.17 sec)

mysql> show create table T\G
*************************** 1. row ***************************
       Table: T
Create Table: CREATE TABLE `T` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `mn` int(10) DEFAULT NULL,
  `mx` int(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `mn_mx` (`mn`,`mx`)
) ENGINE=InnoDB AUTO_INCREMENT=1048561 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> select * from T order by rand() limit 1;
+--------+-----------+-----------+
| id     | mn        | mx        |
+--------+-----------+-----------+
| 112940 | 948004986 | 948004989 |
+--------+-----------+-----------+
1 row in set (0.65 sec)

mysql> explain select id from T where 948004987 between mn and mx;
+----+-------------+-------+-------+---------------+-------+---------+------+--------+--------------------------+
| id | select_type | table | type  | possible_keys | key   | key_len | ref  | rows   | Extra                    |
+----+-------------+-------+-------+---------------+-------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | T     | range | mn_mx         | mn_mx | 5       | NULL | 239000 | Using where; Using index |
+----+-------------+-------+-------+---------------+-------+---------+------+--------+--------------------------+
1 row in set (0.00 sec)

mysql> select id from T where 948004987 between mn and mx;
+--------+
| id     |
+--------+
| 112938 |
| 112939 |
| 112940 |
| 112941 |
+--------+
4 rows in set (0.03 sec)

在我的例子中,我只是增加了mn值的范围,然后将mx设置为+3,这就是为什么我得到的值超过1,但是应该对你应用相同的值。

修改2

重新设计查询肯定会更好

mysql> explain select id from T where mn<=947892055 and mx>=947892055;
+----+-------------+-------+-------+---------------+-------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key   | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+-------+---------+------+------+--------------------------+
|  1 | SIMPLE      | T     | range | mn_mx         | mn_mx | 5       | NULL |    9 | Using where; Using index |
+----+-------------+-------+-------+---------------+-------+---------+------+------+--------------------------+

值得注意的是,即使第一个explain报告许多要扫描的行,我也有足够的innodb缓冲池设置,以便在创建后将整个内容保留在RAM中;所以它仍然很快。

答案 1 :(得分:0)

如果您的设置中没有间隙,则可以使用简单的gte比较:

SELECT ID FROM T WHERE ? >= MN ORDER BY MN ASC LIMIT 1