MySQL 拒绝在 SET 子句中使用 SELECT 对 UPDATE 查询使用索引

标签 mysql mariadb query-performance

我需要根据用户的 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/

相关文章:

MySQl如何在一列中使用多个运算符

mysql - SQL 不返回任何记录

android - Android 无法连接 Mysql 数据库

mysql - 具有多个条件的复杂 SQL 查询

mysql - SQL 查询占用 100% CPU - Mariadb

mysql - 这是真的吗 : "In MySQL, fetching a column value forces a read of the entire row"?

php - 建立与CakePHP数据库的连接时出错

mysql - 检查 string1 的任何部分是否与 string2 的任何部分匹配

mysql - 同一表在不同数据库中不使用索引

mysql - 改进查询以根据没有联合的值连接其他表