如何在单个查询中使用索引进行多表连接?

Posted

技术标签:

【中文标题】如何在单个查询中使用索引进行多表连接?【英文标题】:How to use indexing for multiple table join in a single query? 【发布时间】:2019-05-04 08:56:54 【问题描述】:

具有多个表连接的查询花费了太多时间。如何为下面的查询做索引:

查询:

select  ri.id, LOWER(REPLACE(ri.name,' ','-')) as urlName,ri.name,
ri.logo,
group_concat(distinct rc.cuisine order by  rc.cuisine asc SEPARATOR ',
        '
            ) as 'cuisine_string', rc.cuisine, rai.rating, rai.min_order_amount,
        rai.latitude, rai.longitude, rai.delivery_time, rdf.start_dist,
rdf.end_dist, rdf.fee, ri.address_search, rai.delivery_facility,
ri.status as 'restaurant_status', rt.day, rt.status 'opening_status',
rt.opening_time, rt.closing_time,
' - ',rdf.end_dist,
' km',' : ','₹',fee) SEPARATOR '~') as 'delivery_fee_string',
GROUP_CONCAT(distinct CONCAT(rdf.start_dist, ( SELECT  MATCH (ri.address_search) AGAINST ('Kahilipara,
                Guwahati, Assam, India') as relevance
            from  restaurant_info ri
            where  ri.id = rai.restaurant_id
              and  ri.id = rt.restaurant_id
              and  ri.id = rdf.restaurant_id
              and  ri.id = rc.restaurant_id) as ord , ( 3959 * acos ( cos ( radians(26.1428694) ) * cos( radians( rai.latitude ) ) * cos( radians( rai.longitude ) - radians(91.768487) ) + sin ( radians(26.1428694) ) * sin( radians( rai.latitude ) ) ) ) AS distance
    from  restaurant_info ri
    inner join  restaurant_additional_info rai  ON ri.id = rai.restaurant_id
    inner join  restaurant_timing rt  ON ri.id = rt.restaurant_id
    inner join  restaurant_delivery_fee rdf  ON ri.id = rdf.restaurant_id
    inner join  restaurant_cuisine rc  ON ri.id = rc.restaurant_id
    where  ri.status = 1
      and  rt.status = 1
      and  rt.day = lower(DATE_FORMAT(NOW(),'%a'))
      and  rai.delivery_facility != 1
      and  rai.min_order_amount <= 100
      and  rai.rating <= ''
      and  MATCH (ri.address_search) AGAINST ('Kahilipara, Guwahati,
                Assam, India'
                 )
      and  rt.opening_time < '12:40:21'
      and  rt.closing_time > '12:40:21'
    group by  ri.id
    having  rdf.start_dist = 0
      and  distance < 3.10686
    order by  distance asc
    LIMIT  100 OFFSET 0

问题描述 - 此查询需要 8.5 秒才能运行。我的应用程序包含 1-2 个这样的查询。因此在服务器上加载时间接近 1 分钟。

谁能帮我在这个查询上应用索引?

【问题讨论】:

如果您真的需要性能,请查看 mysql 的地理空间支持。 在查询中使用 EXPLAIN ......但我想这是消耗时间的计算。有多少记录? 2000 多个记录在那里,我正在为 3 个表使用连接 @LarsStegelitz @TimBiegeleisen 这个问题不是任何人的重复。我根据表格上输入的地址进行获取,然后将其与我的表格进行匹配。 请尝试重复链接中的一种或多种索引技术。 【参考方案1】:

为各种表格设置SHOW CREATE TABLE 会有所帮助。另外,EXPLAIN SELECT ...

where  ri.status = 1
  and  rt.status = 1
  and  rt.day = lower(DATE_FORMAT(NOW(),'%a'))
  and  rai.delivery_facility != 1
  and  rai.min_order_amount <= 100
  and  rai.rating <= ''
  and  MATCH (ri.address_search) AGAINST ('Kahilipara, Guwahati, Assam, India')
  and  rt.opening_time < '12:40:21'
  and  rt.closing_time > '12:40:21'
group by  ri.id
having  rdf.start_dist = 0
  and  distance < 3.10686
order by  distance asc
LIMIT  100 OFFSET 0

由于WHERE 子句中有FULLTEXT 条件,它可能会首先执行此操作。但是,如果这些区域有数千行,我们需要进一步挖掘。

rai.rating &lt;= '' 看起来很奇怪;你对“

having rdf.start_dist = 0 -- 似乎这属于WHERE 子句,而不是HAVING

一个大问题是范围测试分散在多个表中:

rt -- just a certain day of the week
rai -- limited amount, etc
ri -- certain regions
rt -- time range, complicated by using 2 columns

即使这些字段在同一个表中,计算成本仍然很高。

distance &lt; ... -- 如链接中所述,这可以使用“边界框”进行优化,但由于其他问题,它不会有太大帮助。

JOIN ... GROUP BY id -- 这可能是一个“爆炸-内爆”场景。发生的事情是JOINs 通过加入 1:many 等来扩展正在查看的“行”数。然后GROUP BY 试图回到每个 id 仅一行。如果不是用于所有过滤,则有一些解决方法。

同时,GROUP BY 可能使用不当。见ONLY_FULL_GROUP_BY

order by distance LIMIT .. -- 在排序和交付最多 100 行之前,查询被强制收集所有行(数千?)。

rt.day = lower(DATE_FORMAT(NOW(),'%a')) - 使用合适的排序规则,您就不需要LOWER()

MATCH (ri.address_search) ..distance &lt; .. 似乎是多余的。

为什么有 5 张桌子,每张桌子都用“餐厅 id”作为关键字? (可能有正当理由,但看起来很可疑。)它们是 1:1 还是 1:many?

【讨论】:

他们是 1:1 的关系。这就是为什么我们在每张桌子上都输入了餐厅 ID。 @里克詹姆斯

以上是关于如何在单个查询中使用索引进行多表连接?的主要内容,如果未能解决你的问题,请参考以下文章

5MySQL多表查询

多表查询与索引

TypeORM 无关联关系的mysql多表连接查询

多表查询(章节摘要)

MybatisPlus多表连接查询一对多分页查询数据

09_MySQL笔记-组函数-GROUP BY-LIMIT-多表连接查询-子查询-UNION-索引-视图-存储过程