I will try to describe my problem as simple as I can.
I have a db with tables that contains columns with phone numbers ranges, e.g. numbertype_id = 7
, number_start = '00110000'
, number_end = '00119999'
, numbers = 10000
(of course I have an id pk and unique key index as well).
On the site that uses those tables, I have a search functionality, which should find a number within those ranges (if there is any). For example, user searches for '00112233', and it should return true, since there is a hit.
Now the problem is performance. I am searching only in one table per search, with a max of under 200k rows.
Being varchar
columns (and need to be, since with int
I would loose the leading 0's, plus the leading zeros could be none, up to 6-7), for me the obvious choice was a search like this:
SELECT *
FROM numberdata
WHERE numbertype_id = 7
AND number_start <= '00112233'
AND number_end >= '00112233'
Depending on the table, that could take anywhere between 1.5 to 2.5 seconds, and it's not normal to take that long.
Does someone knows a better solution for that?
IMPORTANT UPDATE
I did not explained one vital detail. No matter that the number searched for returns any result or now, I still have to "offer" 45 alternatives, and have to search for those 45 numbers in part as well. So the total queries are 46 actually.
SHOW CREATE TABLE
returns this
CREATE TABLE 'numberdata' (
'id' int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'The primary identifier',
'numbertype_id' int(10) unsigned NOT NULL DEFAULT '0',
'prefix' varchar(5) NOT NULL DEFAULT '',
'number_start' varchar(20) NOT NULL DEFAULT '',
'number_end' varchar(20) NOT NULL DEFAULT '',
'number_count' int(10) unsigned NOT NULL DEFAULT '0',
'owner' varchar(100) DEFAULT NULL,
'owner_city' varchar(100) DEFAULT NULL,
PRIMARY KEY ('id'),
UNIQUE KEY number ('numbertype_id','number_start','number_end')
) ENGINE=InnoDB AUTO_INCREMENT=48690 DEFAULT CHARSET=utf8
And EXPLAIN SELECT...
returns this
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE numberdata ref number number 4 const 17174 Using index condition
-
Is it possible for the query to return 2 or more rows? Or will it always return 1 (or 0) rows?
For any number I return only one result, or none (in case it's not present in any of the ranges). No possibilities for duplicates.
-
Can we assume that all values in number_start and number_end are left-padded with 0s to the same length?
Yes, that's correct.
Best Answer
Assumptions:
the query can only return 1 row (or none) and never 2 or more, i.e. the
(number_start, number_end)
ranges are not overlapping for the samenumbertype_id
.the
number_start
andnumber_end
values are all left-padded with 0s to the same length.Under the above assumptions, we can use a trick to use an index to fetch exactly 1 row (if there is one) that matches the criteria (not all, checking only the low end of the range) - and don't search for more rows. The check on the high end is done before the row is returned to avoid any false positive result:
The query will only need an index on
(numbertype_id, number_start)
- or keep the one you have on(numbertype_id, number_start, number_end)
.