【问题标题】:MySQL Query is taking a very long timeMySQL Query 需要很长时间
【发布时间】:2018-02-26 08:39:10
【问题描述】:

我有一个查询,它获取给定位置周围的站点列表,并从另一个表中搜索与查询的站点具有相同坐标的行的 ID,并连接所有具有相同坐标的“行”列ID 作为从第二个表中查询的 ID。

查询如下所示

SELECT Instance,
Namespace, 
Stations.Name, 
Stations.Lat, 
Stations.Lon, 
Temp, 
Humid, 
(SELECT ID FROM transportData.stations WHERE stations.Lat = Stations.Lat AND stations.Lon = Stations.Lon) AS 'StationID',
( 6371 * acos( cos( radians(44.436292) ) * cos( radians( Stations.Lat ) ) * cos( radians( Stations.Lon ) - radians(26.102452) ) + sin( radians(44.436292) ) * sin(radians(Stations.Lat)) ) ) AS distance,
(SELECT GROUP_CONCAT(Line SEPARATOR ';') FROM transportData.timeTables WHERE timeTables.Station = StationID GROUP BY timeTables.Station) AS 'Lines'
FROM `Stations`
WHERE `Namespace` LIKE "%8" 
HAVING distance < 0.1
ORDER BY distance

但是,这个查询只返回一个站点(在提供的坐标的 100 m 半径内)执行需要 58 秒。

关于如何优化它的任何建议?

编辑: 车站表:

+-----------+---------------+------+-----+---------+-------+
| Field     | Type          | Null | Key | Default | Extra |
+-----------+---------------+------+-----+---------+-------+
| Instance  | varchar(6)    | NO   | PRI | NULL    |       |
| Namespace | varchar(10)   | NO   |     | NULL    |       |
| Name      | varchar(255)  | NO   |     | NULL    |       |
| Lat       | decimal(9,6)  | NO   |     | NULL    |       |
| Lon       | decimal(9,6)  | NO   |     | NULL    |       |
| Temp      | float         | YES  |     | NULL    |       |
| Humid     | float         | YES  |     | NULL    |       |
| Bio       | varchar(3000) | YES  |     | NULL    |       |
| Photo     | varchar(1000) | YES  |     | NULL    |       |
| Phone     | varchar(255)  | YES  |     | NULL    |       |
| Website   | varchar(255)  | YES  |     | NULL    |       |
| OpenHours | varchar(1024) | YES  |     | NULL    |       |
+-----------+---------------+------+-----+---------+-------+

transportData.stations 表:

+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| ID    | int(11)     | NO   | PRI | NULL    |       |
| Name  | varchar(50) | NO   |     | NULL    |       |
| Lat   | varchar(15) | NO   |     | NULL    |       |
| Lon   | varchar(15) | NO   |     | NULL    |       |
| Type  | varchar(20) | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+

时间表:

+----------+---------------+------+-----+---------+----------------+
| Field    | Type          | Null | Key | Default | Extra          |
+----------+---------------+------+-----+---------+----------------+
| ID       | int(11)       | NO   | PRI | NULL    | auto_increment |
| Line     | varchar(10)   | NO   |     | NULL    |                |
| Station  | int(11)       | NO   |     | NULL    |                |
| Weekdays | varchar(3500) | NO   |     | NULL    |                |
| Weekends | varchar(3500) | NO   |     | NULL    |                |
+----------+---------------+------+-----+---------+----------------+

编辑:我尝试使用一些 MariaDB 空间函数,发现它可以工作 ST_Distance(point(44.436292,26.102452), point(Lat, Lon)),但它给了我奇怪的读数,与来自我以前有的公式。这可能是因为ST_Distance返回的是平面上的距离,没用,因为它没有考虑地球的曲率。

【问题讨论】:

  • 请显示您的表结构和索引结构。
  • 该查询正在执行大量计算,并且当您在计算列上进行操作时,它需要进行表扫描。这些必然比索引版本慢。如果您正在做大量的 GIS 工作,请使用MySQL Spatial Extensions
  • 您能否以某种方式根据值过滤电台...如果距离为 0.1 公里...那么您要查找的电台的经度和纬度应该接近您的输入...应该会显着限制您的计算
  • @Martin 我已经添加了表结构。
  • 好的,如果您使用 GPS 数据,有两个选择:1)您有一个小数据集并且知道大部分输出,因此您可以弥补一些东西;或者 2) 你真的使用空间数据,你使用空间列和空间索引(如果你需要和你的老板争论的话,我们谈论将 50s 更改为

标签: mysql sql database optimization


【解决方案1】:

根据您的意见,这是我能提供的最佳答案:

/!\我无法测试它,所以我希望它能按你的意愿工作。

我放了一个索引来加快 WHERE 的速度,并假设您将搜索 &lt; 0.1km

CREATE INDEX small_miracle 
    ON `Stations`(`Namespace`, `Lat`, `Lon`);

SELECT 
    Instance,
    Namespace, 
    Stations.Name, 
    Stations.Lat, 
    Stations.Lon, 
    Temp, 
    Humid, 
    TDS.ID AS 'StationID',
    ( 
        6371 
        * acos( 
              cos( radians(44.436292) ) 
            * cos( radians( Stations.Lat ) ) 
            * cos( 
                radians( Stations.Lon ) 
                - radians(26.102452) 
                ) 
            + sin( radians(44.436292) ) 
            * sin(radians(Stations.Lat))
            ) 
        ) AS distance,
    timeTables.Lines AS 'Lines'
FROM `Stations`
INNER JOIN transportData.stations TDS
    ON (
        TDS.Lat = Stations.Lat
        AND TDS.Lon = Stations.Lon
        )
LEFT JOIN (
    SELECT 
        timeTables.Station,
        GROUP_CONCAT(Line SEPARATOR ';') Lines
    FROM transportData.timeTables 
    GROUP BY timeTables.Station
) timeTables
    ON timeTables.Station = TDS.ID 
WHERE 
    `Namespace` LIKE "%8" 
    AND 26.102452 +0.1 > Stations.Lat
    AND 26.102452 -0.1 < Stations.Lat
    AND 44.436292 +0.1 > Stations.Lon
    AND 44.436292 -0.1 < Stations.Lon
    AND distance < 0.1
ORDER BY distance;

【讨论】:

  • 我尝试使用一些 MariaDB 空间函数,发现它可以工作ST_Distance(point(44.436292,26.102452), point(Lat, Lon)),但它给了我奇怪的读数,与我的公式中的读数不相似。 PS:你的查询不起作用,还没弄清楚原因
  • @GeorgeLazu 尝试使用ST_Distance 编辑您的问题。我的查询会引发错误吗?你能提供创建表和数据样本来做一些测试吗? (比如 5 到 10 行)
  • pastebin.com/SyYw6VGe 这是几行的表结构
  • @GeorgeLazu 我无法让您的查询与此数据样本一起工作sqlfiddle.com/#!9/ef0d2d/12 这正常吗?
  • 这是因为获取数据集为“StationID”的查询部分已被注释。不再重要了,我已经改变了我的方法,当我需要它们时我会查询少量信息,而不是一次对我需要的每条信息都使用那个大查询。
猜你喜欢
  • 1970-01-01
  • 2023-02-23
  • 2015-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-18
相关资源
最近更新 更多