我使用 MySQL 5.5,并且创建了 3 个表用于测试:
- 属性(entity_id、cid、aid、value)- 索引:全部
- 商品(entity_id、价格、货币)- 索引:entity_id
- 汇率(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
我读了很多关于比较 UNION
与 JOIN
与 IN()
的主题,最好的结果给出了第二个选项,但它太慢了一直以来。
有什么方法可以在这里获得更好的性能吗?为什么这么慢? 我是否应该考虑将一些逻辑(将此查询拆分为 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/