MySQL查询时间过长,花费了大量时间

时间:2014-10-21 10:23:42

标签: php mysql time query-optimization

我是stackoverflow中的新手。 我的查询花了很多时间(超过5分钟)。 这个查询可以变得更快吗? 请!帮我!我不知道:(

我有2个表插座和销售: 1.出口:具有唯一数据的表格       - id_outlet       - name_outlet 2.销售:像插座的细节(1个插座有很多数据)       - id_outlet       - msisdn(1个出口超过1 msisdn)       - 日期

所以,我需要获取数据: 1.分支 2.插座数量 3.活动插座数量(我从本月检查现有插座获得此数据) 3.计数msisdn 4.后退检查插座的数量(我从上个月检查现有插座获得此数据,但本月不存在) 5.新活动插座的数量(我从本月检查现有插座获得此数据,但在上个月不存在) 6.非活动插座的数量(我从上个月和本月检查该插座不存在获得此数据) 7. Consistant Outlet的数量(我从本月和上个月检查现有商店获得此数据)

这是我的查询:

SELECT branch br, COUNT(DISTINCT id_outlet) AS tot_outlet,
                    (SELECT COUNT(DISTINCT(id_outlet))
                        FROM outlet o
                        WHERE ".$outlet['status'][$i]." AND active=1 AND branch = br
                        AND id_outlet IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$thisMonth."%')) AS aktif,
                    (SELECT COUNT(msisdn) FROM sales s, outlet o
                        WHERE ".$outlet['status'][$i]." and s.date LIKE '".$thisMonth."%' AND o.active=1 AND s.id_outlet = o.id_outlet
                         AND s.branch=br) AS supply,
                    (SELECT COUNT(DISTINCT(id_outlet))
                        FROM outlet o
                        WHERE ".$outlet['status'][$i]." AND active=1 AND branch = br
                        AND id_outlet IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$lastMonth."%')
                        AND id_outlet NOT IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$thisMonth."%')) AS back_checking,
                    (SELECT COUNT(DISTINCT(id_outlet))
                        FROM outlet o
                        WHERE ".$outlet['status'][$i]." AND active=1 AND branch = br
                        AND id_outlet NOT IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$lastMonth."%')
                        AND id_outlet IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$thisMonth."%')) AS new_aktif,
                    (SELECT COUNT(DISTINCT(id_outlet))
                        FROM outlet o
                        WHERE ".$outlet['status'][$i]." AND active=1 AND branch = br
                        AND id_outlet NOT IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$lastMonth."%')
                        AND id_outlet NOT IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$thisMonth."%')) AS non_aktif,
                    (SELECT COUNT(DISTINCT(id_outlet))
                        FROM outlet o
                        WHERE ".$outlet['status'][$i]." AND active=1 AND branch = br
                        AND id_outlet IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$lastMonth."%')
                        AND id_outlet IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$thisMonth."%')) AS konsisten
                FROM outlet o
                WHERE ".$outlet['status'][$i]." AND active=1 
                GROUP BY branch

2 个答案:

答案 0 :(得分:0)

自己诊断的几个步骤。

解释您的疑问。

要解释您的查询,这很简单;您只需要在EXPLAIN部分的前面添加SELECT

运行此;

EXPLAIN SELECT branch br, COUNT(DISTINCT id_outlet) AS tot_outlet,
                (SELECT COUNT(DISTINCT(id_outlet))
                    FROM outlet o
                    WHERE ".$outlet['status'][$i]." AND active=1 AND branch = br
                    AND id_outlet IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$thisMonth."%')) AS aktif,
                (SELECT SUM(featurephone) FROM outlet o
                    WHERE ".$outlet['status'][$i]." 
                    AND active=1 AND branch = br) AS feature,
                (SELECT SUM(smartphone) FROM outlet o
                    WHERE ".$outlet['status'][$i]." 
                    AND active=1 AND branch = br) AS smart,
                (SELECT COUNT(msisdn) FROM sales s, outlet o
                    WHERE ".$outlet['status'][$i]." and s.date LIKE '".$thisMonth."%' AND o.active=1 AND s.id_outlet = o.id_outlet
                     AND s.branch=br) AS supply,
                (SELECT COUNT(DISTINCT(id_outlet))
                    FROM outlet o
                    WHERE ".$outlet['status'][$i]." AND active=1 AND branch = br
                    AND id_outlet IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$lastMonth."%')
                    AND id_outlet NOT IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$thisMonth."%')) AS back_checking,
                (SELECT COUNT(DISTINCT(id_outlet))
                    FROM outlet o
                    WHERE ".$outlet['status'][$i]." AND active=1 AND branch = br
                    AND id_outlet NOT IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$lastMonth."%')
                    AND id_outlet IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$thisMonth."%')) AS new_aktif,
                (SELECT COUNT(DISTINCT(id_outlet))
                    FROM outlet o
                    WHERE ".$outlet['status'][$i]." AND active=1 AND branch = br
                    AND id_outlet NOT IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$lastMonth."%')
                    AND id_outlet NOT IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$thisMonth."%')) AS non_aktif,
                (SELECT COUNT(DISTINCT(id_outlet))
                    FROM outlet o
                    WHERE ".$outlet['status'][$i]." AND active=1 AND branch = br
                    AND id_outlet IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$lastMonth."%')
                    AND id_outlet IN (SELECT DISTINCT id_outlet FROM sales WHERE DATE LIKE '".$thisMonth."%')) AS konsisten
            FROM outlet o
            WHERE ".$outlet['status'][$i]." AND active=1 
            GROUP BY branch

这将提供类似于此的输出:

Explain View

简要说明您应该寻找什么。最重要的是typepossible_keyskey。主要是type

Type的值可以告诉您它是如何查找查询的。列表(按从最好到最差的顺序)是:system, const, eq_ref, ref, fulltext, ref_or_null, unique_subquery, index_subquery, range, index, all。你不想要任何全部;而且最有可能的是,主要关注ref和eq_ref。由于查询的结构,您可能会有子查询。

要解决这个问题,我要做的是查看表的结构和查询。任何将从排序列表(基本上是索引)中受益的东西都会被索引。对我来说,这意味着我将索引任何外键ID(即使你不使用外键,设置索引);任何属性,如删除,可见或其他。

简单地说,我将大多数在查询的where部分中进行比较的内容编入索引。这是一个好的开始;从那里,你真的应该优化的东西,因为索引可以使用大量的内存,但这是性能提升的良好开端。

答案 1 :(得分:0)

避免在子查询中使用子查询,那么你应该能够做到这样的事情: -

SELECT branch br, COUNT(DISTINCT id_outlet) AS tot_outlet,
                    (SELECT COUNT(DISTINCT(id_outlet))
                        FROM outlet o
                        INNER JOIN sales s2 ON o.id_outlet = s2.id_outlet AND s2.DATE LIKE '".$thisMonth."%'
                        WHERE ".$outlet['status'][$i]." 
                        AND o.active=1 
                        AND o.branch = br) AS aktif,
                    (SELECT COUNT(msisdn) FROM sales s, outlet o
                        WHERE ".$outlet['status'][$i]." 
                        and s.date LIKE '".$thisMonth."%' 
                        AND o.active=1 
                        AND s.id_outlet = o.id_outlet
                         AND s.branch=br) AS supply,
                    (SELECT COUNT(DISTINCT(id_outlet))
                        FROM outlet o
                        INNER JOIN sales s1 ON o.id_outlet = s1.id_outlet AND s1.DATE LIKE '".$lastMonth."%'
                        LEFT OUTER JOIN sales s2 ON o.id_outlet = s2.id_outlet AND s2.DATE LIKE '".$thisMonth."%'
                        WHERE ".$outlet['status'][$i]." 
                        AND o.active=1 
                        AND o.branch = br
                        AND s2.id_outlet IS NULL) AS back_checking,
                    (SELECT COUNT(DISTINCT(id_outlet))
                        FROM outlet o
                        LEFT OUTER JOIN sales s1 ON o.id_outlet = s1.id_outlet AND s1.DATE LIKE '".$lastMonth."%'
                        INNER JOIN sales s2 ON o.id_outlet = s2.id_outlet AND s2.DATE LIKE '".$thisMonth."%'
                        WHERE ".$outlet['status'][$i]." 
                        AND o.active=1 
                        AND o.branch = br
                        AND s1.id_outlet IS NULL) AS new_aktif,
                    (SELECT COUNT(DISTINCT(o.id_outlet))
                        FROM outlet o
                        LEFT OUTER JOIN sales s1 ON o.id_outlet = s1.id_outlet AND s1.DATE LIKE '".$lastMonth."%'
                        LEFT OUTER JOIN sales s2 ON o.id_outlet = s2.id_outlet AND s2.DATE LIKE '".$thisMonth."%'
                        WHERE ".$outlet['status'][$i]." 
                        AND o.active=1 
                        AND o.branch = br
                        AND s1.id_outlet IS NULL
                        AND s2.id_outlet IS NULL) AS non_aktif,
                    (SELECT COUNT(DISTINCT(o.id_outlet))
                        FROM outlet o
                        INNER JOIN sales s1 ON o.id_outlet = s1.id_outlet AND s1.DATE LIKE '".$lastMonth."%'
                        INNER JOIN sales s2 ON o.id_outlet = s2.id_outlet AND s2.DATE LIKE '".$thisMonth."%'
                        WHERE ".$outlet['status'][$i]." AND o.active=1 AND o.branch = br) AS konsisten
                FROM outlet o
                WHERE ".$outlet['status'][$i]." AND active=1 
                GROUP BY branch

这仍然在SELECT中使用多个子查询,但是根据WHERE子句(即$ outlet ['status'] [$ i]的值),这可能不会太糟糕。

我知道你说你已经做到了这一点,但它给出了错误的结果,但是如果没有示例数据和表格布局,我无法做任何检查以确定这应该是错误的原因或原因。

有可能将连接放在主查询中,至少替换最后4个子查询: -

SELECT branch br, COUNT(DISTINCT id_outlet) AS tot_outlet,
                    (SELECT COUNT(DISTINCT(id_outlet))
                        FROM outlet o
                        INNER JOIN sales s2 ON o.id_outlet = s2.id_outlet AND s2.DATE LIKE '".$thisMonth."%'
                        WHERE ".$outlet['status'][$i]." 
                        AND o.active=1 
                        AND o.branch = br) AS aktif,
                    (SELECT COUNT(msisdn) FROM sales s, outlet o
                        WHERE ".$outlet['status'][$i]." 
                        and s.date LIKE '".$thisMonth."%' 
                        AND o.active=1 
                        AND s.id_outlet = o.id_outlet
                         AND s.branch=br) AS supply,
                    COUNT(DISTINCT IF(s1.id_outlet IS NOT NULL AND s2.id_outlet IS NULL, o.id_outlet, NULL) AS back_checking
                    COUNT(DISTINCT IF(s1.id_outlet IS NULL AND s2.id_outlet IS NOT NULL, o.id_outlet, NULL) AS new_aktif
                    COUNT(DISTINCT IF(s1.id_outlet IS NULL AND s2.id_outlet IS NULL, o.id_outlet, NULL) AS non_aktif
                    COUNT(DISTINCT IF(s1.id_outlet IS NOT NULL AND s2.id_outlet IS NOT NULL, o.id_outlet, NULL) AS konsisten
                FROM outlet o
                LEFT OUTER JOIN sales s1 ON o.id_outlet = s1.id_outlet AND s1.DATE LIKE '".$lastMonth."%'
                LEFT OUTER JOIN sales s2 ON o.id_outlet = s2.id_outlet AND s2.DATE LIKE '".$thisMonth."%'
                WHERE ".$outlet['status'][$i]." AND active=1 
                GROUP BY branch

这依赖于仅计算非空值的COUNT(某个表达式)。

EDIT。

一个选项是将核心子查询更改为非核心子查询,您可以对其进行连接。然后,每次只执行一次,而不是每次返回一次。缺点是加入子查询结果不会使用索引,因此连接可能不会那么快。哪个更有效取决于数据量。

使用我之前的查询并更改atkif和supply字段以使用非核心子查询,可以得到: -

SELECT o.branch br, 
    COUNT(DISTINCT o.id_outlet) AS tot_outlet, 
    COALESCE(sub_aktif.aktif, 0) AS aktif, 
    COALESCE(sub_supply.supply, 0) AS supply, 
    COUNT(DISTINCT IF(s1.id_outlet IS NOT NULL AND s2.id_outlet IS NULL, o.id_outlet, NULL)) AS back_checking,
    COUNT(DISTINCT IF(s1.id_outlet IS NULL AND s2.id_outlet IS NOT NULL, o.id_outlet, NULL)) AS new_aktif,
    COUNT(DISTINCT IF(s1.id_outlet IS NULL AND s2.id_outlet IS NULL, o.id_outlet, NULL)) AS non_aktif,
    COUNT(DISTINCT IF(s1.id_outlet IS NOT NULL AND s2.id_outlet IS NOT NULL, o.id_outlet, NULL)) AS konsisten 
FROM outlet o 
LEFT OUTER JOIN
(
    SELECT o.branch, COUNT(DISTINCT(o.id_outlet)) AS aktif
    FROM outlet o 
    INNER JOIN sales s 
    ON o.id_outlet = s.id_outlet 
    WHERE (o.STATUS LIKE 'MALL%' OR o.STATUS LIKE 'STREET%' OR o.STATUS LIKE 'TOP TEN%' OR o.STATUS LIKE 'LIFESTYLE%') 
    AND s.DATE LIKE '2014-10%' AND o.active=1 
    GROUP BY o.branch 
) sub_aktif
ON sub_aktif.branch = o.branch
LEFT OUTER JOIN
(
    SELECT s.branch, COUNT(msisdn) AS supply
    FROM sales s
    INNER JOIN outlet o 
    ON s.id_outlet = o.id_outlet
    WHERE (o.STATUS LIKE 'MALL%' OR o.STATUS LIKE 'STREET%' OR o.STATUS LIKE 'TOP TEN%' OR o.STATUS LIKE 'LIFESTYLE%') 
    AND s.date LIKE '2014-10%' AND o.active=1 
    GROUP BY s.branch 
) sub_supply
ON sub_supply.branch = o.branch
LEFT OUTER JOIN sales s1 ON o.id_outlet = s1.id_outlet AND s1.DATE LIKE '2014-09%' 
LEFT OUTER JOIN sales s2 ON o.id_outlet = s2.id_outlet AND s2.DATE LIKE '2014-10%' 
WHERE (o.STATUS LIKE 'MALL%' OR o.STATUS LIKE 'STREET%' OR o.STATUS LIKE 'TOP TEN%' OR o.STATUS LIKE 'LIFESTYLE%') 
AND active=1 
GROUP BY o.branch ;

更改原始查询以对所有字段执行此操作会产生以下结果: -

SELECT o.branch br, COUNT(DISTINCT id_outlet) AS tot_outlet,
    COALESCE(sub_aktif.aktif, 0) AS aktif, 
    COALESCE(sub_supply.supply, 0) AS supply, 
    COALESCE(sub_back_checking.back_checking, 0) AS back_checking, 
    COALESCE(sub_new_aktif.new_aktif, 0) AS new_aktif, 
    COALESCE(sub_non_aktif.non_aktif, 0) AS non_aktif, 
    COALESCE(sub_konsisten.konsisten, 0) AS konsisten
FROM outlet o
LEFT OUTER JOIN
(
    SELECT o.branch, COUNT(DISTINCT(o.id_outlet)) AS aktif
    FROM outlet o 
    INNER JOIN sales s 
    ON o.id_outlet = s.id_outlet 
    WHERE (o.STATUS LIKE 'MALL%' OR o.STATUS LIKE 'STREET%' OR o.STATUS LIKE 'TOP TEN%' OR o.STATUS LIKE 'LIFESTYLE%') 
    AND s.DATE LIKE '2014-10%' AND o.active=1 
    GROUP BY o.branch 
) sub_aktif
ON sub_aktif.branch = o.branch
LEFT OUTER JOIN
(
    SELECT s.branch, COUNT(msisdn) AS supply
    FROM sales s
    INNER JOIN outlet o 
    ON s.id_outlet = o.id_outlet
    WHERE (o.STATUS LIKE 'MALL%' OR o.STATUS LIKE 'STREET%' OR o.STATUS LIKE 'TOP TEN%' OR o.STATUS LIKE 'LIFESTYLE%') 
    AND s.date LIKE '2014-10%' AND o.active=1 
    GROUP BY s.branch 
) sub_supply
ON sub_supply.branch = o.branch
LEFT OUTER JOIN
(
    SELECT o.branch, COUNT(DISTINCT(o.id_outlet)) AS back_checking
    FROM outlet o
    INNER JOIN sales s1
    ON s1.id_outlet = o.id_outlet AND s1.DATE LIKE '2014-09%'
    LEFT OUTER JOIN sales s2
    ON s2.id_outlet = o.id_outlet AND s2.DATE LIKE '2014-10%'
    WHERE (o.STATUS LIKE 'MALL%' OR o.STATUS LIKE 'STREET%' OR o.STATUS LIKE 'TOP TEN%' OR o.STATUS LIKE 'LIFESTYLE%') AND active=1 
    AND s2.id_outlet IS NULL
    GROUP BY o.branch
) sub_back_checking
ON sub_back_checking.branch = o.branch
LEFT OUTER JOIN
(
    SELECT o.branch, COUNT(DISTINCT(o.id_outlet)) AS new_aktif
    FROM outlet o
    LEFT OUTER JOIN sales s1
    ON s1.id_outlet = o.id_outlet AND s1.DATE LIKE '2014-09%'
    INNER JOIN sales s2
    ON s2.id_outlet = o.id_outlet AND s2.DATE LIKE '2014-10%'
    WHERE (o.STATUS LIKE 'MALL%' OR o.STATUS LIKE 'STREET%' OR o.STATUS LIKE 'TOP TEN%' OR o.STATUS LIKE 'LIFESTYLE%') AND active=1 
    AND s1.id_outlet IS NULL
    GROUP BY o.branch
) sub_new_aktif
ON sub_new_aktif.branch = o.branch
LEFT OUTER JOIN
(
    SELECT o.branch, COUNT(DISTINCT(o.id_outlet)) AS non_aktif
    FROM outlet o
    LEFT OUTER JOIN sales s1
    ON s1.id_outlet = o.id_outlet AND s1.DATE LIKE '2014-09%'
    LEFT OUTER JOIN sales s2
    ON s2.id_outlet = o.id_outlet AND s2.DATE LIKE '2014-10%'
    WHERE (o.STATUS LIKE 'MALL%' OR o.STATUS LIKE 'STREET%' OR o.STATUS LIKE 'TOP TEN%' OR o.STATUS LIKE 'LIFESTYLE%') AND active=1 
    AND s1.id_outlet IS NULL
    AND s2.id_outlet IS NULL
    GROUP BY o.branch
) sub_non_aktif
ON sub_non_aktif.branch = o.branch
LEFT OUTER JOIN
(
    SELECT o.branch, COUNT(DISTINCT(o.id_outlet)) AS konsisten
    FROM outlet o
    INNER JOIN sales s1
    ON s1.id_outlet = o.id_outlet AND s1.DATE LIKE '2014-09%'
    INNER JOIN sales s2
    ON s2.id_outlet = o.id_outlet AND s2.DATE LIKE '2014-10%'
    WHERE (o.STATUS LIKE 'MALL%' OR o.STATUS LIKE 'STREET%' OR o.STATUS LIKE 'TOP TEN%' OR o.STATUS LIKE 'LIFESTYLE%') AND active=1 
    GROUP BY o.branch
) sub_konsisten
ON sub_konsisten.branch = o.branch
WHERE (o.STATUS LIKE 'MALL%' OR o.STATUS LIKE 'STREET%' OR o.STATUS LIKE 'TOP TEN%' OR o.STATUS LIKE 'LIFESTYLE%') AND active=1 
GROUP BY o.branch;

sql小提琴在这里: -

http://sqlfiddle.com/#!2/1343e/20