MySQL - 如何索引该查询?

标签 mysql performance indexing

我在索引此查询时遇到问题:

SELECT  *,
        (ROUND(SQRT(
           POW(LEAST(ABS(-12 - wdata.x),
           ABS(401 - ABS(-12 - wdata.x))), 2) +
            POW(LEAST(ABS(45 - wdata.y),
            ABS(401 - ABS(45 - wdata.y))), 2)),3)
         ) AS distance
    FROM  odata
    LEFT JOIN  wdata ON wdata.id=odata.vref
    WHERE  TRUE
    HAVING  distance<4.9497474683058326708059105347339
    ORDER BY  distance
    LIMIT  30

结果是:

+----+-------------+-------+--------+---------------+---------+---------+-----------------------------+-------+---------------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref                         | rows  | Extra                           |
+----+-------------+-------+--------+---------------+---------+---------+-----------------------------+-------+---------------------------------+
|  1 | SIMPLE      | odata | ALL    | NULL          | NULL    | NULL    | NULL                        | 19118 | Using temporary; Using filesort |
|  1 | SIMPLE      | wdata | eq_ref | PRIMARY       | PRIMARY | 4       | mytravia_1000-14.odata.vref |     1 | NULL                            |
+----+-------------+-------+--------+---------------+---------+---------+-----------------------------+-------+---------------------------------+
2 rows in set (0.00 sec)

我知道它显示 0.00 秒是执行时间,但是这个查询将运行很多次,并且它显示它会减慢我的数据库,我不知道为什么!

每次我看到此查询的检查行数为 459448,因此出于某些原因,它对我的​​工作非常不利。

谁能给个建议吗?如何为 odata 表建立正确的索引?或者我可以使用子查询来修复它吗?

表格是:

解释一下odata:

vref    int(10) unsigned    NO  PRI     NULL    
type    tinyint(4)  NO      NULL    
conqured    mediumint(8) unsigned   NO      NULL    
wood    float(12,2) NO      NULL    
iron    float(12,2) NO      NULL    
clay    float(12,2) NO      NULL    
woodp   float(12,2) NO      NULL    
ironp   float(12,2) NO      NULL    
clayp   float(12,2) NO      NULL    
maxstore    mediumint(8) unsigned   NO      NULL    
crop    float(12,2) NO      NULL    
cropp   float(12,2) NO      NULL    
maxcrop     mediumint(8) unsigned   NO      NULL    
lasttrain   int(10) unsigned    NO      NULL    
lastfarmed  int(10) unsigned    NO      NULL    
lastupdated     int(10) unsigned    NO      NULL    
loyalty     tinyint(4)  NO      100 
owner   smallint(5) unsigned    NO      2   
name    char(45)    NO      Oasis   

并解释 wdata:

id  int(10) unsigned    NO  PRI     NULL    auto_increment
fieldtype   tinyint(3)  NO      NULL    
oasistype   tinyint(3)  NO      NULL    
x   smallint(5) NO  MUL     NULL    
y   smallint(5) NO  MUL     NULL    
occupied    tinyint(4)  NO      NULL    
image   char(12)    NO  MUL     NULL    
pos     tinyint(3)  NO  MUL     NULL    

我不得不说 wdata.id 和 odata.vref 已经编入索引!

表结构->

CREATE TABLE IF NOT EXISTS `odata` (
  `vref` int(10) unsigned NOT NULL,
  `type` tinyint(4) NOT NULL,
  `conqured` mediumint(8) unsigned NOT NULL,
  `wood` float(12,2) NOT NULL,
  `iron` float(12,2) NOT NULL,
  `clay` float(12,2) NOT NULL,
  `woodp` float(12,2) NOT NULL,
  `ironp` float(12,2) NOT NULL,
  `clayp` float(12,2) NOT NULL,
  `maxstore` mediumint(8) unsigned NOT NULL,
  `crop` float(12,2) NOT NULL,
  `cropp` float(12,2) NOT NULL,
  `maxcrop` mediumint(8) unsigned NOT NULL,
  `lasttrain` int(10) unsigned NOT NULL,
  `lastfarmed` int(10) unsigned NOT NULL,
  `lastupdated` int(10) unsigned NOT NULL,
  `loyalty` tinyint(4) NOT NULL DEFAULT '100',
  `owner` smallint(5) unsigned NOT NULL DEFAULT '2',
  `name` char(45) NOT NULL DEFAULT 'Unoccupied Oasis',
  PRIMARY KEY (`vref`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

对于 wdata 来说是 ->

CREATE TABLE IF NOT EXISTS `wdata` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `fieldtype` tinyint(3) NOT NULL,
  `oasistype` tinyint(3) NOT NULL,
  `x` smallint(5) NOT NULL,
  `y` smallint(5) NOT NULL,
  `occupied` tinyint(4) NOT NULL,
  `image` char(12) NOT NULL,
  `pos` tinyint(3) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `x` (`x`),
  KEY `y` (`y`),
  KEY `image` (`image`),
  KEY `pos` (`pos`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=160802 ;

致以诚挚的问候。

最佳答案

目前没有任何索引可以加快查询速度。目前,它必须对两个表JOINing结果中的每一行计算SQRT

通过在执行任何JOINing之前找到关闭30,您将获得一些改进:

SELECT  *, distance
  FROM  ( SELECT id,
    (ROUND(SQRT(
       POW(LEAST(ABS(-12 - wdata.x),
       ABS(401 - ABS(-12 - wdata.x))), 2) +
        POW(LEAST(ABS(45 - wdata.y),
        ABS(401 - ABS(45 - wdata.y))), 2)),3)
     ) AS distance
    FROM  wdata
    HAVING  distance<4.9497474683058326708059105347339
    ORDER BY  distance
    LIMIT 30
      ) w
  JOIN odata ON w.id=odata.vref
  ORDER BY  w.distance

这将需要 id 和 vref 建立索引。

下一项改进是将搜索限制在至少一个方向:

AND x >= -12 - 4.94...
AND x <= -12 + 4.94...

并且在wdata中具有复合索引INDEX(x, id)。 (抱歉,我不知道“401”在公式中的位置。)

如果这还不够快,则解决方案为 more complicated .

关于MySQL - 如何索引该查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33713747/

相关文章:

html - 渲染 html 表格最快的方法是什么?

azure - 有没有办法查看 Azure CosmosDB 中当前的索引状态

php - mysql_connect() : Headers and client library minor version mismatch Library:100005

sql - 从一个表中查找另一个表中不存在的记录

python - 为什么我的 Go 程序在以下场景中的性能比预期差很多?

python - 使用 *args 和指定的列表索引对列表求和

MySQL 后缀索引

javascript - 我的 sql 内容到 javascript 数组

php - 通过使用 PHP 选择复选框来删除多行

python - 使用 Numpy 和 Cython 加速距离矩阵计算