【问题标题】:MySQL - How to index this query?MySQL - 如何索引此查询?
【发布时间】:2016-02-16 06:35:18
【问题描述】:

我无法索引此查询:

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

结果是:

+----+-------------+--------+--------+------------- --+---------+---------+-------------- --+--------+----------------------------------+ |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+--------+--------+------------- --+---------+---------+-------------- --+--------+----------------------------------+ | 1 |简单 |数据 |全部 |空 |空 |空 |空 | 19118 |使用临时的;使用文件排序 | | 1 |简单 |数据 | eq_ref |初级 |初级 | 4 | mytravia_1000-14.odata.vref | 1 |空 | +----+-------------+--------+--------+------------- --+---------+---------+-------------- --+--------+----------------------------------+ 2 行(0.00 秒)

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

每次我看到这个查询检查的行是 459448,所以它在某些方面对我的工作非常不利。

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

表格是:

解释 odata:

vref int(10) 无符号 NO PRI NULL 键入 tinyint(4) 无 NULL 征服 mediumint(8) 无符号 NO NULL 木浮子(12,2)无空 铁浮子(12,2)NO NULL 粘土浮子(12,2)NO NULL woodp float(12,2) 无空值 Ironp 浮点数(12,2) 无 NULL 粘土浮子(12,2)无空 maxstore mediumint(8) 无符号 NO NULL 作物浮动(12,2)NO NULL 裁剪浮动(12,2)NO NULL maxcrop mediumint(8) 无符号 NO NULL lasttrain int(10) 无符号 NO NULL lastfarmed int(10) 无符号 NO NULL lastupdated int(10) 无符号 NO NULL 忠诚 tinyint(4) NO 100 所有者 smallint(5) 无符号 NO 2 名称 char(45) NO Oasis

并解释wdata:

id int(10) 无符号 NO PRI NULL auto_increment 字段类型 tinyint(3) NO NULL oasistype tinyint(3) NO NULL x smallint(5) NO MUL NULL y smallint(5) NO MUL NULL 占用 tinyint(4) NO NULL 图像 char(12) NO MUL NULL pos tinyint(3) NO MUL NULL

我不得不说 wdata.id 和 odata.vref 已经被索引了!

表结构->

如果不存在 `odata` 则创建表( `vref` int(10) 无符号非空, `type` tinyint(4) NOT NULL, `conqured` mediumint(8) unsigned NOT NULL, `wood` float(12,2) NOT NULL, `iron` float(12,2) NOT NULL, `粘土`浮动(12,2)不为空, `woodp` float(12,2) NOT NULL, `ironp` float(12,2) NOT NULL, `clayp` float(12,2) 非空, `maxstore` mediumint(8) 无符号非空, `crop` float(12,2) NOT NULL, `cropp` float(12,2) NOT NULL, `maxcrop` mediumint(8) 无符号非空, `lasttrain` int(10) 无符号非空, `lastfarmed` int(10) 无符号非空, `lastupdated` int(10) 无符号非空, `loyalty` tinyint(4) NOT NULL DEFAULT '100', `owner` smallint(5) unsigned NOT NULL DEFAULT '2', `name` char(45) NOT NULL DEFAULT 'Unoccupied Oasis', 主键(`vref`) ) 引擎=InnoDB 默认字符集=utf8;

而对于 wdata 是 ->

如果不存在 `w​​data` 则创建表( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `fieldtype` tinyint(3) NOT NULL, `oasistype` tinyint(3) NOT NULL, `x` smallint(5) 非空, `y` smallint(5) 非空, `占用` tinyint(4) NOT NULL, `image` char(12) NOT NULL, `pos` tinyint(3) 非空, 主键(`id`), 键`x`(`x`), 键`y`(`y`), KEY `image` (`image`), 键`pos`(`pos`) ) 引擎=InnoDB 默认字符集=utf8 AUTO_INCREMENT=160802 ;

最好的问候。

【问题讨论】:

    标签: mysql performance indexing


    【解决方案1】:

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

    您将通过在执行任何JOINingJOINing 之前找到关闭 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

    【讨论】:

      【解决方案2】:

      就像 Rick James 所说,您不能索引 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 位的数字。根据需要进行调整。)

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

      (样本数据:

      • fieldtype=1
      • oasistype=1
      • x=10
      • y=11
      • 占用=1
      • image='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)";
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-13
        • 1970-01-01
        • 1970-01-01
        • 2021-12-21
        • 2012-02-01
        相关资源
        最近更新 更多