草庐IT

MYSQL查询优化,多查询还是一次大查询

coder 2023-10-25 原文

我有一个查询,其中有一些子查询(内部选择),我正在尝试找出哪个对性能更好,一个较大的查询或许多较小的查询,我发现很难尝试对差异进行计时因为它在我的服务器上一直在变化。

我使用下面的查询一次返回 10 个结果以显示在我的网站上,使用分页(偏移和限制)。

SELECT adverts.*, breed.breed, breed.type, sellers.profile_name, sellers.logo, users.user_level , 
round( sqrt( ( ( (adverts.latitude - '51.558430') * (adverts.latitude - '51.558430') ) * 69.1 * 69.1 ) + ( (adverts.longitude - '-0.0069345') * (adverts.longitude - '-0.0069345') * 53 * 53 ) ), 1 ) as distance, 
( SELECT advert_images.image_name FROM advert_images WHERE advert_images.advert_id = adverts.advert_id AND advert_images.main = 1 LIMIT 1) as imagename, 
( SELECT count(advert_images.advert_id) from advert_images WHERE advert_images.advert_id = adverts.advert_id ) AS num_photos 
FROM adverts 
LEFT JOIN breed ON adverts.breed_id = breed.breed_id 
LEFT JOIN sellers ON (adverts.user_id = sellers.user_id) 
LEFT JOIN users ON (adverts.user_id = users.user_id) 
WHERE (adverts.status = 1) AND (adverts.approved = 1) 
AND (adverts.latitude BETWEEN 51.2692837281 AND 51.8475762719) AND (adverts.longitude BETWEEN -0.472015213613 AND 0.458146213613) 
having (distance <= '20') 
ORDER BY distance ASC 
LIMIT 0,10

从主查询中删除下面的 2 个内部选择,然后在我的 php 循环中,调用 2 个选择 10 次,循环中的每条记录一次,这样会更好吗?

( SELECT advert_images.image_name FROM advert_images WHERE advert_images.advert_id = adverts.advert_id AND advert_images.main = 1 LIMIT 1) as imagename, 
( SELECT count(advert_images.advert_id) from advert_images WHERE advert_images.advert_id = adverts.advert_id ) AS num_photos 

最佳答案

避免子查询

据我了解,您的内部选择有两个用途:查找关联图像的任何名称,以及计算关联图像的数量。您可能可以使用左连接而不是内部选择来实现这两个目标:

SELECT …,
      advert_images.image_name AS imagename,
      COUNT(advert_images.advert_id) AS num_photos,
      …
FROM …
     LEFT JOIN advert_images ON advert_images.advert_id = adverts.advert_id
…
GROUP BY adverts.advert_id
…
LIMIT 0,10

我还没有尝试过这个,但也许 MySQL 引擎足够聪明,可以只对您实际返回的行执行那部分查询。

请注意,对于给定的图像集,此查询将返回哪个图像名称完全没有保证。如果你想要可重现的结果,你应该在那里使用一些聚合函数,例如MIN(advert_images.image_name) 选择字典顺序的第一张图片。

单独选择但不循环

如果上述方法不起作用,即查询仍将检查 advert_images 表以获取计算结果的所有行,那么您可能真的会更好执行第二个查询。但是,您可以尝试避免 for 循环,而是在单个查询中获取所有这些行:

SELECT advert_images.image_name AS imagename,
       COUNT(advert_images.advert_id) AS num_photos
FROM advert_images
WHERE advert_images.advert_id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
GROUP BY advert_images.advert_id

此查询中的十个参数对应于您当前生成的结果的十行。请注意,没有相关照片的广告将不会包含在该结果中。因此,请确保在您的代码中将 num_photos 默认为零,并将 imagename 默认为 NULL

临时表

实现您尝试做的事情的另一种方法是使用显式临时内存表:首先选择您感兴趣的结果,然后检索所有相关信息。

CREATE TEMPORARY TABLE tmp
SELECT adverts.advert_id, round(…) as distance
FROM adverts
WHERE (adverts.status = 1) AND (adverts.approved = 1)
  AND (adverts.latitude BETWEEN 51.2692837281 AND 51.8475762719)
  AND (adverts.longitude BETWEEN -0.472015213613 AND 0.458146213613)
HAVING (distance <= 20)
ORDER BY distance ASC
LIMIT 0,10;

SELECT tmp.distance, adverts.*, …
       advert_images.image_name AS imagename,
       COUNT(advert_images.advert_id) AS num_photos,
       …
FROM tmp
     INNER JOIN adverts ON tmp.advert_id = adverts.advert_id
     LEFT JOIN breed ON adverts.breed_id = breed.breed_id
     LEFT JOIN sellers ON adverts.user_id = sellers.user_id
     LEFT JOIN users ON adverts.user_id = users.user_id
     LEFT JOIN advert_images ON advert_images.advert_id = adverts.advert_id
GROUP BY adverts.advert_id
ORDER BY tmp.distance ASC;

DROP TABLE tmp;

这将确保仅针对您当前正在处理的结果查询所有其他表。毕竟,advert_images 表除了您可能需要其中的多行之外没有什么神奇之处。

子查询作为连接因子

基于上一段的方法,您甚至可以避免管理临时表,而使用子查询代替:

SELECT sub.distance, adverts.*, …
       advert_images.image_name AS imagename,
       COUNT(advert_images.advert_id) AS num_photos,
       …
FROM ( SELECT adverts.advert_id, round(…) as distance
        FROM adverts
        WHERE (adverts.status = 1) AND (adverts.approved = 1)
          AND (adverts.latitude BETWEEN 51.2692837281 AND 51.8475762719)
          AND (adverts.longitude BETWEEN -0.472015213613 AND 0.458146213613)
        HAVING (distance <= 20)
        ORDER BY distance ASC
        LIMIT 0,10;
     ) AS sub
     INNER JOIN adverts ON sub.advert_id = adverts.advert_id
     LEFT JOIN breed ON adverts.breed_id = breed.breed_id 
     LEFT JOIN sellers ON (adverts.user_id = sellers.user_id) 
     LEFT JOIN users ON (adverts.user_id = users.user_id) 
     LEFT JOIN advert_images ON advert_images.advert_id = adverts.advert_id
GROUP BY adverts.advert_id
ORDER BY sub.distance ASC

您再次通过仅使用 adverts 表中的数据来确定相关行,并仅连接其他表中的所需行。最有可能的是,该中间结果将在内部存储在一个临时表中,但这由 SQL 服务器决定。

关于MYSQL查询优化,多查询还是一次大查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12583751/

有关MYSQL查询优化,多查询还是一次大查询的更多相关文章

  1. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

    我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

  2. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  3. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  4. ruby-on-rails - 在 Rails 和 ActiveRecord 中查询时忽略某些字段 - 2

    我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr

  5. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  6. sql - 查询忽略时间戳日期的时间范围 - 2

    我正在尝试查询我的Rails数据库(Postgres)中的购买表,我想查询时间范围。例如,我想知道在所有日期的下午2点到3点之间进行了多少次购买。此表中有一个created_at列,但我不知道如何在不搜索特定日期的情况下完成此操作。我试过:Purchases.where("created_atBETWEEN?and?",Time.now-1.hour,Time.now)但这最终只会搜索今天与那些时间的日期。 最佳答案 您需要使用PostgreSQL'sdate_part/extractfunction从created_at中提取小时

  7. ruby-on-rails - Rake 任务仅调用一次时执行两次 - 2

    我写了一个非常简单的rake任务来尝试找到这个问题的根源。namespace:foodotaskbar::environmentdoputs'RUNNING'endend当在控制台中执行rakefoo:bar时,输出为:RUNNINGRUNNING当我执行任何rake任务时会发生这种情况。有没有人遇到过这样的事情?编辑上面的rake任务就是写在那个.rake文件中的所有内容。这是当前正在使用的Rakefile。requireFile.expand_path('../config/application',__FILE__)OurApp::Application.load_tasks这里

  8. ruby - 我怎样才能只写一次 "Text"并同时检查 path_info 是否包含 'A' ? - 2

    -if!request.path_info.include?'A'%{:id=>'A'}"Text"-else"Text"“文本”写了两次。我怎样才能只写一次并同时检查path_info是否包含“A”? 最佳答案 有两种方法可以做到这一点。使用部分,或使用content_forblock:如果“文本”较长,或者是一个重要的子树,您可以将其提取到一个部分。这会使您的代码变干一点。在给出的示例中,这似乎有点矫枉过正。在这种情况下更好的方法是使用content_forblock,如下所示:-if!request.path_info.inc

  9. ruby-on-rails - Ruby 流量控制 : throw an exception, 返回 nil 还是让它失败? - 2

    我在思考流量控制的最佳实践。我应该走哪条路?1)不要检查任何东西并让程序失败(更清晰的代码,自然的错误消息):defself.fetch(feed_id)feed=Feed.find(feed_id)feed.fetchend2)通过返回nil静默失败(但是,“CleanCode”说,你永远不应该返回null):defself.fetch(feed_id)returnunlessfeed_idfeed=Feed.find(feed_id)returnunlessfeedfeed.fetchend3)抛出异常(因为不按id查找feed是异常的):defself.fetch(feed_id

  10. ruby-on-rails - 无法安装 mysql2 0.3.14 gem - 2

    我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby​​目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin

随机推荐