使用子查询优化SQL

时间:2012-10-14 11:50:14

标签: mysql sql sql-optimization

我想优化一个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秒。

screenshot

我的想法是将子查询编写为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条记录

1 个答案:

答案 0 :(得分:1)

不完全确定,但是如果你要使用内部表(你是第一个例子),这里有许多帮助,你真的必须尝试缩小它所撤回的内容。这可能意味着写一些冗余的where子句。例如,“order_dt在2001和2002之间”可能会在较小的内部表和主查询中结束。为什么?因为内部表在某些情况下无法优化 - 导致服务器临时检索数百万行,只需要少数几行。

另外,我注意到你的一个连接中的字符串函数“LEFT(f.denumire,450))”

在连接期间应该避免任何类型的功能,这会强制服务器(逐个)评估每个记录...它无法优化。这有点类似于为什么你应该总是使用主键进行连接,但更耗时...最好坚持“喜欢”= AND,NOT,OR,IN ..等等。