我有一个简单的查询。我所要做的就是将地理位置数据组合到一个主表中,这样我就不需要在提取数据时使用连接。是的,加入很好,但我想把所有数据放在一个表中......
这是我的问题:
> explain UPDATE test AS l
SET l.country_name1 = (
select country_name
from ip2location_db9
where
l.ip >= ip_from
ORDER BY ip_from DESC
LIMIT 1)
******************** 1. row *********************
id: 1
select_type: UPDATE
table: l
partitions:
type: ALL
possible_keys:
key:
key_len:
ref:
rows: 10
filtered: 100.00
Extra:
******************** 2. row *********************
id: 2
select_type: DEPENDENT SUBQUERY
table: ip2location_db9
partitions:
type: index
possible_keys: idx_ip_from,idx_ip_from_to,ip_from_to,ip_from
key: idx_ip_from
key_len: 5
ref:
rows: 1
filtered: 33.33
Extra: Using where; Backward index scan
2 rows in set
简单吧?除了这10行的查询需要130多秒? 单独运行时的select语句超快(0.00秒)并且工作正常。
explain select country_name
from ip2location_db9
where
ip_from <= INET_ATON('114.160.63.108')
ORDER BY ip_from DESC
LIMIT 1
******************** 1. row *********************
id: 1
select_type: SIMPLE
table: ip2location_db9
partitions:
type: range
possible_keys: idx_ip_from,idx_ip_from_to
key: idx_ip_from
key_len: 5
ref:
rows: 1949595
filtered: 100.00
Extra: Using index condition
1 rows in set
但是,当我在测试表中只使用14行的update语句时,查询会花费130秒。
表格设置如下:
CREATE TABLE `test` (
`ip` int(10) unsigned DEFAULT NULL,
`country_name` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`region_name` varchar(128) COLLATE utf8_bin DEFAULT NULL,
`city_name` varchar(128) COLLATE utf8_bin DEFAULT NULL,
`latitude` double DEFAULT NULL,
`longitude` double DEFAULT NULL,
`zip_code` varchar(30) COLLATE utf8_bin DEFAULT NULL,
`data` varchar(512) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `ip2location_db9`(
`ip_from` INT(10) UNSIGNED,
`ip_to` INT(10) UNSIGNED,
`country_code` CHAR(2),
`country_name` VARCHAR(64),
`region_name` VARCHAR(128),
`city_name` VARCHAR(128),
`latitude` DOUBLE,
`longitude` DOUBLE,
`zip_code` VARCHAR(30),
INDEX `idx_ip_from` (`ip_from`),
INDEX `idx_ip_to` (`ip_to`),
INDEX `idx_ip_from_to` (`ip_from`, `ip_to`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
请注意,在运行更新查询时,硬盘驱动器活动会变得疯狂,利用率很高。 看到?没有什么花哨。表数据匹配。行正确编入索引。
解释说明使用索引就像我自己执行选择时一样:
******************** 1. row *********************
id: 1
select_type: UPDATE
table: l
partitions:
type: ALL
possible_keys:
key:
key_len:
ref:
rows: 14
filtered: 100.00
Extra:
******************** 2. row *********************
id: 2
select_type: DEPENDENT SUBQUERY
table: ip2location_db93
partitions:
type: index
possible_keys: idx_ip_from,idx_ip_from_to
key: idx_ip_from
key_len: 5
ref:
rows: 1
filtered: 33.33
Extra: Using where
2 rows in set
那么我可能做错了什么会导致查询花费2分钟来运行14行? 它不是硬件。 i7 6990,32gb ram,跑掉ssd。这不是世界上最快的,但我可以比这个查询更快地手动更新14行......
我花了很多时间搜索,试图找出为什么这么长时间。我假设我没有正确搜索。也许我不知道一个特定的术语或某些东西会指向我正确的方向。我不是数据库的人。只是为了工作而做这件事。
希望你们能保住我的理智......
添加更多信息......
我试图让这个查询工作很多方面。堆栈上的其他问题表示要避免子查询并使用连接。好的,这是我尝试的第一件事,但我不能让查询使用我建立的索引。
> explain UPDATE test AS l
JOIN
ip2location_db9 AS h
ON
l.ip <= h.ip_from and l.ip >= h.ip_to
SET
l.country_name1 = h.country_name
******************** 1. row *********************
id: 1
select_type: UPDATE
table: l
partitions:
type: ALL
possible_keys: ip
key:
key_len:
ref:
rows: 10
filtered: 100.00
Extra:
******************** 2. row *********************
id: 1
select_type: SIMPLE
table: h
partitions:
type: ALL
possible_keys: idx_ip_from,idx_ip_to,idx_ip_from_to,ip_from_to,ip_from,ip_to
key:
key_len:
ref:
rows: 3495740
filtered: 11.11
Extra: Range checked for each record (index map: 0x3F)
2 rows in set
使用联接的更新是否比这更容易?
即使使用强制索引也不会使查询使用索引。
我已经尝试了很多方法
UPDATE test
JOIN (
SELECT * FROM ip2location_db9
) AS t1
ON (test.ip <= t1.ip_from and test.ip >= t1.ip_to)
SET test.country_name1 = t1.country_name
UPDATE test l,
(SELECT
*
FROM
ip2location_db9,test
WHERE
test.ip >= ip_from
ORDER BY ip_from DESC
LIMIT 1) AS PreQualified
SET
l.country_name1 = PreQualified.country_name
什么都行不通!我做错了什么?