0

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
4

1 回答 1

3

您的 SQL 包含一些重大漏洞,$_REQUEST["distance"]并且可能包含$subwhereclause,具体取决于它的来源。

此外,请考虑使用PDO或 mysqli,因为:

已弃用:mysql 扩展已弃用,将来将被删除。

无论如何,要回答您的问题,请尝试使用explain并查看 mysql 告诉您的内容。它会告诉您正在使用哪些索引以及正在扫描多少行。这应该让你走上正确的轨道。

我建议从您的 Shell 或 PHPMyAdmin 执行此操作。

祝你好运!

PS - 要使用explain只需将其添加到SELECT语句的开头。

EXPLAIN SELECT * FROM table WHERE column=value

编辑 - 我看到你已经运行EXPLAIN并发布了结果。

possible_keys并且key非常重要(键与索引相同)。解释是分析每个查询(因为有一个子查询)。

possible_keys为 NULL 就像 Mysql 说“我查看了每个索引以查看是否可以使用它,但它们都不起作用”。通常这意味着您必须进行全表扫描(rows 是需要扫描的行数)。

子查询尤其受到缺少可用键的影响。它必须扫描84173 行。由于您的查询并不完全简单,因此这可能是对每一行的非常密集的扫描。

看看你的索引,找出为什么 mysql 不能使用它们。我建议在 PHPMyAdmin 或 Shell 中执行此操作的原因是现在您可以轻松更改查询并查看索引是否有效。尝试先简化所有内容,然后再重新构建。

于 2013-08-01T02:13:05.707 回答