我需要根据用户的 IP,使用 geoip
表中的国家/地区名称填充 users
表中的 location
字段。
这是表的创建代码。
CREATE TABLE `geoip` (
`IP_FROM` INT(10) UNSIGNED ZEROFILL NOT NULL DEFAULT '0000000000',
`IP_TO` INT(10) UNSIGNED ZEROFILL NOT NULL DEFAULT '0000000000',
`COUNTRY_NAME` VARCHAR(50) NOT NULL DEFAULT '',
PRIMARY KEY (`IP_FROM`, `IP_TO`)
)
ENGINE=InnoDB;
CREATE TABLE `users` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`login` VARCHAR(25) NOT NULL DEFAULT ''
`password` VARCHAR(64) NOT NULL DEFAULT ''
`ip` VARCHAR(128) NULL DEFAULT ''
`location` VARCHAR(128) NULL DEFAULT ''
PRIMARY KEY (`id`),
UNIQUE INDEX `login` (`login`),
INDEX `ip` (`ip`(10))
)
ENGINE=InnoDB
ROW_FORMAT=DYNAMIC;
我尝试运行的更新查询是:
UPDATE users u
SET u.location =
(SELECT COUNTRY_NAME FROM geoip WHERE INET_ATON(u.ip) BETWEEN IP_FROM AND IP_TO)
问题是这个查询拒绝在 geoip
表上使用 PRIMARY
索引,尽管它会大大加快速度。 EXPLAIN 给我:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY u index NULL PRIMARY 4 NULL 1254395
2 DEPENDENT SUBQUERY geoip ALL PRIMARY NULL NULL NULL 62271 Using where
我最终将 geoip
表转换为仅用于此查询的 MEMORY 引擎,但我想知道什么是正确的方法。
更新 我使用的 DBMS 是 MariaDB 10.0.17,如果它能有所作为的话。
最佳答案
你有没有试过这样强制索引
UPDATE users u
SET u.location =
(SELECT COUNTRY_NAME FROM geoip FORCE INDEX (PRIMARY)
WHERE INET_ATON(u.ip) BETWEEN IP_FROM AND IP_TO)
此外,由于 ip 可以为 NULL,因此它可能会影响索引优化。
关于MySQL 拒绝在 SET 子句中使用 SELECT 对 UPDATE 查询使用索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29624514/