mysql - JOIN 与 UNION 与 IN() - 大表和许多 WHERE 条件

标签 mysql sql

我使用 MySQL 5.5,并且创建了 3 个表用于测试:

  1. 属性(entity_id、cid、aid、value)- 索引:全部
  2. 商品(entity_id、价格、货币)- 索引:entity_id
  3. 汇率(currency_from、currency_to、汇率)- 索引:NONE

我需要计算指定条件的结果(按属性搜索)并选择按某列排序的 X 行。 查询应支持在项目属性(属性表)中搜索。

我一开始有一个这样的查询:

SELECT i.entity_id, i.price * COALESCE(r.rate, 1) AS final_price 
FROM items i
JOIN attributes a ON a.entity_id = i.entity_id
LEFT JOIN rates r ON i.currency = r.currency_from AND r.currency_to = 'EUR'
WHERE a.cid = 4 AND ( (a.aid >= 10 AND a.value > 2000) OR (a.aid <= 10 AND a.value > 5) )
HAVING final_price BETWEEN 0 AND 9000
ORDER BY final_price DESC
LIMIT 20

但在大表上速度相当慢。 where 条件可以更大(甚至可以达到 30 个参数),并且有时可以使用 CAST(a.value as SIGNED) 来使用 BETWEEN(对于范围值)。

例如:

SELECT 
      i.entity_id, 
      i.price * COALESCE(r.rate, 1) AS final_price 
   FROM 
      attributes a 
         JOIN items i
            ON a.entity_id = i.entity_id 
         LEFT JOIN rates r 
            ON i.currency = r.currency_from 
            AND r.currency_to = 'EUR'
   WHERE 
      a.cid = 4 AND ( 
(a.aid = 10 AND CAST(a.value AS SIGNED) BETWEEN 2000 AND 2014) 
OR (a.aid = 121 AND CAST(a.value AS SIGNED) BETWEEN 40 AND 60) 
OR (a.aid = 45 AND CAST(a.value AS SIGNED) BETWEEN 770 AND 1500) 
OR (a.aid = 95 AND CAST(a.value AS SIGNED) BETWEEN 12770 AND 15500) 
OR (a.aid = 98 AND a.value = 'some value') 
OR (a.aid = 199 AND a.value = 'some another value') 
OR (a.aid = 102 AND a.value = 1)
OR (a.aid = 112 AND a.value = 42) ) 
   GROUP BY
      i.entity_id
   HAVING 
      COUNT(i.entity_id) = 7 
         AND final_price BETWEEN 0 AND 9000
   ORDER BY 
      final_price DESC
   LIMIT 20

我按 COUNT() 等于 7(要搜索的属性数)进行分组,因为我需要查找具有所有这些属性的项目。

解释基本查询(第一个):

id  select_type table   type    possible_keys   key key_len ref rows    Extra   
1   SIMPLE  a   ALL entity_id,value NULL    NULL    NULL    379999  Using where; Using temporary; Using filesort
1   SIMPLE  i   eq_ref  PRIMARY PRIMARY 4   testowa.a.entity_id 1   Using where
1   SIMPLE  r   ALL NULL    NULL    NULL    NULL    2   

我读了很多关于比较 UNIONJOININ() 的主题,最好的结果给出了第二个选项,但它太慢了一直以来。

有什么方法可以在这里获得更好的性能吗?为什么这么慢? 我是否应该考虑将一些逻辑(将此查询拆分为 3 个小查询)移至后端(php/ror)代码?

最佳答案

我会稍微重组您的查询并首先获得属性表 然后加入到项目中。另外,我会有一个覆盖索引 项目表通过(entity_id,价格)和属性表上的索引 ON(cid、aid、value、entity_id)和您的费率表索引 ON(货币来源、货币目标、汇率)。这样,所有的索引都覆盖了 并且引擎不需要去原始数据页面来获取数据,它可以 从已经用于连接/条件的索引中提取它。

SELECT 
      i.entity_id, 
      i.price * COALESCE(r.rate, 1) AS final_price 
   FROM 
      attributes a 
         JOIN items i
            ON a.entity_id = i.entity_id 
         LEFT JOIN rates r 
            ON i.currency = r.currency_from 
            AND r.currency_to = 'EUR'
   WHERE 
      a.cid = 4 AND ( (a.aid >= 10 AND a.value > 2000) OR (a.aid <= 10 AND a.value > 5) )
   HAVING 
      final_price BETWEEN 0 AND 9000
   ORDER BY 
      final_price DESC
   LIMIT 20

所以,虽然这对您提供的查询有帮助,但您能否显示其他一些您将有更多标准条件的地方...您提到它可能超过 30 个(或更多)。查看更多可能会改变查询咯。

对于具有多个条件的更新查询,我会为“a.cid = 4”之后的所有“aid”值添加一个 IN() 子句。这样,在它必须满足所有“OR”条件之前,如果它因“援助”不是您考虑的条件而失败,则它永远不必满足那些...例如

      a.cid = 4 
   AND a.id in ( 10, 121, 45, 95, 98, 199, 102 )
   AND  ( rest of the complex aid, casting and between criteria )

关于mysql - JOIN 与 UNION 与 IN() - 大表和许多 WHERE 条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23399496/

相关文章:

MySQL - 替换列值

sql - 排除包含特定值的行

PHP:数组搜索组

sql - 插入...选择*,如何忽略身份?

c# - MySQL 未在 VS Express 2012 版 Entity Framework 的数据库更新模型中列出

mysql - 如何根据 aspirasi_id 计算百分比

MySQL:枚举与 Varchar-with-Index

mysql - 在 tomcat7 中部署 WAR 文件时出现错误

android - 每个用户一个 mysql 数据库,数千个用户

sql - 复制列内容 PostgreSQL 7.4