TL;DR - 使用 JOIN
代替低效的 IN
我有一个程序可以返回列为“覆盖”原始位置一定距离内的任何位置的单元。
即用户搜索区域、距离和类别。然后,该查询将返回覆盖原始区域搜索距离内的任何区域的所有办事处。
有两个表格,一个是“出版物/办公室”,另一个是每个出版物涵盖的地区,由它们的索引链接。在区域数据库中,出版物涵盖的每个区域都有一个条目。
问题是,当我运行查询时,最多需要 3 分钟才能返回结果?
我正在使用共享云服务器,但我觉得代码效率低下。任何帮助将不胜感激!
<?php
}
$sql=mysql_query("select * from publications where ".$subwhereclause." AND publications.entry_id in (( SELECT regions_to_publications.pub_id from regions_to_publications WHERE ((ACOS(SIN($lat * PI() / 180) * SIN(lat * PI() / 180) + COS($lat * PI() / 180) * COS(lat * PI() / 180) * COS(($lon - lon) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) *(1.6) < ".$_REQUEST["distance"].")) ") or die(mysql_error());
?>
<?php while($row3= mysql_fetch_array($sql)) {?>
<div class="result-entry-card">
<p class="card-title"><?php echo $row3['entry_name'] ;?></p>
<p class="card-cat"><?php echo $row3['entry_category'];?></p>
<p><?php echo $row3['entry_phone'];?> - <a href="mailto:<?php echo $row3['entry_email'];?>"><?php echo $row3['entry_email'];?></a></p>
<p><a href="http://<?php echo $row3['entry_website'];?>">Email</a></p>
</div>
<?php } ?>
知道为什么要花这么长时间才能完成吗?我还在学习!
更新:我对以下代码运行了 EXPLAIN
(替换变量)。
EXPLAIN SELECT *
FROM publications
WHERE publications.entry_category
IN (
".newspapers."
)
AND publications.entry_id
IN (
(
SELECT regions_to_publications.pub_id
FROM regions_to_publications
WHERE (
(
ACOS( SIN( - 33.8683 * PI( ) /180 ) * SIN( lat * PI( ) /180 ) + COS( - 33.8683 * PI( ) /180 ) * COS( lat * PI( ) /180 ) * COS( ( 151.2086 - lon ) * PI( ) /180 ) ) *180 / PI( )
) *60 * 1.1515
) * ( 1.6 ) < "1000"
)
)
这些是结果。
| id |select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
| 1 | PRIMARY | publications | ALL | NULL | NULL | NULL | NULL | 621 | Using where |
| 2 | DEPENDENT SUBQUERY | regions_to_publications | ALL | NULL | NULL | NULL | NULL | 84173 | Using where
对于任何想知道的人,我查看了这些结果并重新搜索如下,使用 JOIN
而不是 IN
。
SELECT *
FROM publications
JOIN regions_to_publications ON entry_id = pub_id
WHERE (
(
ACOS( SIN( - 33.8683 * PI( ) /180 ) * SIN( regions_to_publications.lat * PI( ) /180 ) + COS( - 33.8683 * PI( ) /180 ) * COS( regions_to_publications.lat * PI( ) /180 ) * COS( ( 151.2086 - regions_to_publications.lon ) * PI( ) /180 ) ) *180 / PI( )
) *60 * 1.1515
) * ( 1.6 ) < "1000"
AND
publications.entry_category
IN (
"radio"
)
GROUP BY publications.entry_id
最佳答案
您的 SQL 在 $_REQUEST["distance"]
和可能的 $subwhereclause
中包含一些主要漏洞,具体取决于它的来源。
另外,考虑使用 PDO或 mysqli 因为这个:
Deprecated: The mysql extension is deprecated and will be removed in the future.
无论如何,要回答您的问题,请尝试使用 explain
并查看 mysql 告诉您的内容。它会告诉您正在使用哪些索引以及正在扫描多少行。这应该会让您走上正确的轨道。
我建议从您的 Shell 或 PHPMyAdmin 执行此操作。
祝你好运!
附言- 要使用 explain
,只需将其添加到 SELECT
语句的开头即可。
EXPLAIN SELECT * FROM table WHERE column=value
编辑 - 我看到您已经运行了 EXPLAIN
并发布了结果。
possible_keys
和 key
非常重要(键与索引相同)。解释是分析每个查询(因为有一个子查询)。
possible_keys
为 NULL 就像 Mysql 说“我查看了每个索引以查看是否可以使用它,但没有一个有效”。通常这意味着您必须进行全表扫描(行数是需要扫描的行数)。
子查询尤其缺乏可用的键。它必须扫描 84173 行。由于您的查询并不十分简单,因此这可能是对每一行的非常密集的扫描。
查看您的索引并弄清楚为什么 mysql 不能使用它们。我建议在 PHPMyAdmin 或 Shell 中执行此操作的原因是现在您可以轻松更改查询并查看索引是否有效。首先尝试简化所有内容,然后再重新构建。
关于php - 为什么我的 PHP/MYSQL 请求加载最多需要 5 分钟?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17984087/