我想优化一个SQL语句,下面是原始语句。
SELECT DISTINCT
p.productID,
p.filename,
p.model,
p.code,
p.manufacturerID,
f2.manufacturerName,
m.manufacturerName,
CONCAT('INSERT INTO distribItems(productID, manufacturer, code, distributorText) VALUES (',
CAST(p.productID AS CHAR),
', \'',
f2.manufacturerName,
'\', \'',
f2.code,
'\', \'',
f2.denumire,
'\') ;') INS
FROM
(SELECT
f.manufacturerName, f.categoryName, f.code, f.denumire, f.code_2
FROM
furnizorlist f
LEFT JOIN distribitems d ON
(d.manufacturer = f.manufacturerName
AND (d.code = f.code OR d.manufacturer LIKE 'DELL')
AND d.distributorText = LEFT(f.denumire, 450))
WHERE
productID IS NULL) f2,
products p,
manufacturers m
WHERE
f2.code_2 <> ''
AND (f2.code_2 = p.code_2 OR f2.code_2 = p.model_2)
AND p.manufacturerID = m.manufacturerID
AND m.manufacturerName = f2.manufacturerName
AND m.manufacturerName != 'Compatibil'
AND p.code != '1'
ORDER by p.filename ASC;
在我的电脑上大约需要34秒。
我的想法是将子查询编写为Join,并在Where子句中设置条件。
这是我令人难以置信的快速SQL:
SELECT DISTINCT
p.productID,
p.filename,
p.model,
p.code,
p.manufacturerID,
f.manufacturerName,
m.manufacturerName,
CONCAT('INSERT INTO distribItems(productID, manufacturer, code, distributorText) VALUES (',
CAST(p.productID AS CHAR),
', \'',
f.manufacturerName,
'\', \'',
f.code,
'\', \'',
f.denumire,
'\') ;') INS
FROM
furnizorlist f,
distribitems d,
#subquery end
products p,
manufacturers m
WHERE
d.manufacturer = f.manufacturerName
AND (d.code = f.code OR d.manufacturer LIKE 'DELL')
AND d.distributorText = LEFT(f.denumire, 450)
AND d.productID IS NULL
#subquery condions end (f and d tables)
# the next is a subquery result:
AND f.code_2 <> ''
AND (f.code_2 = p.code_2 OR f.code_2 = p.model_2)
AND p.manufacturerID = m.manufacturerID
AND m.manufacturerName = f.manufacturerName
AND m.manufacturerName != 'Compatibil'
AND p.code != '1'
ORDER by p.filename ASC;
如果我写信explain improved_sql
,我会看到不可能的WHERE 列。
我试过但却找不到为什么不可能。我检查了字段的兼容性:没有需要将int与varchar进行比较的情况。我找不到任何重大错误,这就是我在这里的原因。
WHERE子句中是否存在逻辑错误?比如要求某个字段为1
,之后又为2
?
以ID结尾的字段为INT 以NAME结尾的字段,代码为varchar(255) 带文本的字段为450和8192 varchar(仅使用一个地方)
traslation:“denumire”表示“描述” - 或类似的东西:)
不确定哪个版本的mysql正在运行服务器端,可能是5。
如果我有充分的理由,我可以建议表结构更改,可能会调整代码。
编辑:
来自这里的不可能的地方:
EXPLAIN
SELECT
f.manufacturerName,
f.categoryName,
f.code,
f.denumire,
f.code_2
FROM
furnizorlist f
INNER JOIN
distribitems d ON (d.manufacturer = f.manufacturerName
AND (d.code = f.code
OR d.manufacturer LIKE 'DELL')
AND d.distributorText = LEFT(f.denumire, 450))
WHERE
productID IS NULL
备注:INNER JOIN,而不是LEFT JOIN。
EDIT2: 表:furnizorlist 42,751条记录 表:distribitems 72,290条记录
答案 0 :(得分:1)
不完全确定,但是如果你要使用内部表(你是第一个例子),这里有许多帮助,你真的必须尝试缩小它所撤回的内容。这可能意味着写一些冗余的where子句。例如,“order_dt在2001和2002之间”可能会在较小的内部表和主查询中结束。为什么?因为内部表在某些情况下无法优化 - 导致服务器临时检索数百万行,只需要少数几行。
另外,我注意到你的一个连接中的字符串函数“LEFT(f.denumire,450))”
在连接期间应该避免任何类型的功能,这会强制服务器(逐个)评估每个记录...它无法优化。这有点类似于为什么你应该总是使用主键进行连接,但更耗时...最好坚持“喜欢”= AND,NOT,OR,IN ..等等。