MySQL - 如何索引此查询?

时间:2015-11-14 21:44:38

标签: mysql performance indexing

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

#include <iostream>
#include <math.h>
using namespace std;

class Valjak{
private: float r, h;
public: 
    Valjak(){
        r = 1;
        h = 1;
    }
    Valjak(float rr, float hh){
        r = rr;
        h = hh;
    }
    void Oplosje(){
        cout << "Oplosje valjka je: " << 2 * (pow(r, 2)*3.14) + 2 * r*h << endl;
    }
    void Volumen(){
        cout << "Volumen je: " << (pow(r, 2)*3.14) * h << endl;
    }
};

int main(){

    Valjak V1;
    Valjak V2(5, 10);
    cout << "Vrijednosti prvog objekta!" << endl;
    V1.Oplosje();
    V1.Volumen();
    cout << "Vrijednosti drugog objekta" << endl;
    V2.Oplosje();
    V2.Volumen();

    system("PAUSE");
    return 0;
}

结果是:

+----+-------------+-------+--------+---------------+---------+---------+-----------------------------+-------+---------------------------------+
| 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已经编入索引了!

表格结构 - &gt;

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是 - &gt;

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 ;
最好的问候。

2 个答案:

答案 0 :(得分:1)

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

通过在执行任何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...

并在INDEX(x, id)中包含复合索引wdata。 (对不起,我不知道“401”在哪里符合公式。)

如果速度不够快,解决方案将获得more complicated

答案 1 :(得分:1)

就像Rick James所说,你不能索引distance,因为它是动态计算的。

这给你两个问题:1,它很慢,如你所知。 2,你在数据层做了逻辑计算,我不喜欢这个。

我认为这里最好的解决方案是在你做的时候即时计算距离。为什么不在插入/更新wdata和/或x的同时将距离存储在y?将其放在名为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
  • 图像=&#39; ABCDEFGHIJKL&#39;
  • 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语句将是:

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)";