2015-11-14 24 views
0

我有索引麻烦此查询:MySQL - 如何索引此查询?

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 ; 

最诚挚的问候。

回答

1

没有索引会加快查询的速度。它目前必须在JOINing这两个表格的结果中为每一行评估SQRT

您将找到闭合得到一些改善前30 做任何JOINing

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... 

和具有综合指数wdataINDEX(x, id)。 (对不起,我不知道“401”符合公式的地方。)

如果这样不够快,解决方案得到more complicated

1

像里克詹姆斯说,你不能索引distance,因为它是动态计算。

这给你两个问题:1,它是缓慢的,如你所知。 2,你在数据层进行逻辑计算,我不太喜欢。

我认为这里最好的解决方案是而不是就像你在做的那样快速计算距离。在插入/更新x和/或y的同时,为什么不把距离存储在wdata?把它放在名为distance的列中。然后,您可以对该列进行索引,一切都将非常快速。此外,你不会重复计算一遍又一遍,使事情更有效率。最后,您将能够移除数据层的计算并将其放到应用程序级别的更合适的位置。

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, 
    `distance` decimal(32, 24) 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 `distance` (`distance`), 
    KEY `image` (`image`), 
    KEY `pos` (`pos`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=160802; 

(用于远距离的数据类型可以是任何你认为合适的。我用decimal(32, 24)将存储带有多达24位到小数点右边和多达12到左边。根据需要调整。)

然后你会改变你的插入操作是这样的:

(样本数据:

  • 字段类型= 1
  • oasistype = 1
  • X = 10
  • Y = 11
  • 占用= 1个
  • 图像= 'ABCDEFGHIJKL'
  • POS = 1

insert into wdata (fieldtype,oasistype,x,y,distance,occupied, image, pos) 
values (1, 1, 10, 11, (ROUND(SQRT(
      POW(LEAST(ABS(-12 - 10), 
      ABS(401 - ABS(-12 - 10))), 2) + 
      POW(LEAST(ABS(45 - 11), 
      ABS(401 - ABS(45 - 11))), 2)),3) 
     ), 1, 'abcdefghijkl', 1) 

和您的选择语句将是:

SELECT * FROM odata 
    LEFT JOIN wdata ON wdata.id=odata.vref 
    where wdata.distance<4.9497474683058326708059105347339 
    ORDER BY wdata.distance 
    LIMIT 30 

如果你已经在WDATA表一组数据,你不能刚插入,你可以这样做一次更新所有行(你的距离增加新列后):

update wdata set distance = 
    (ROUND(SQRT(
    POW(LEAST(ABS(-12 - x), 
    ABS(401 - ABS(-12 - x))), 2) + 
    POW(LEAST(ABS(45 - y), 
    ABS(401 - ABS(45 - y))), 2)),3)) 

另外值得注意的是,我会从MySQL中删除数学,并让您的应用程序执行它。

例如,在PHP:

$distance = (round(sqrt(pow(min(abs(-12 - 10), abs(401 - abs(-12 - 10))), 2) + pow(min(abs(45 - 11), abs(401 - abs(45 - 11))), 2)),3)); 

$sql = "insert into wdata (fieldtype, oasistype, x, y, distance, occupied, image, pos) 
     values (1, 1, 10, 11, $distance, 1, 'abcdefghijkl', 1)";