MySQL 性能 - 如何加速构建/燃尽图的查询?

MySQL Performance - How to accelerate query for Build-Up / Burn-Down Chart?

我正在 MySQL/PHP 中开发一个应用程序生成累积图和燃尽图,我使用 NVD3.js

生成它

我下面的代码每天循环我的数据集的最小到最大日期,可以过滤。

到目前为止,我 运行 每天查询 1 次,我意识到这会增加大量的延迟,大约 10 秒等待加载。

如何更快地生成此数据?

调用代码

public function burnupAction()
    {
        $actionItemTable = $this->getActionItemTable();
        $burnUp =  array('TotalActionItems' => $actionItemTable->getBurnup('AssignedDate'),
                         'ECDItems' => $actionItemTable->getBurnup('ECD'),
                         'OriginalDueItems' => $actionItemTable->getBurnup('DueDate'),
                         'ActualOpenItems' => $actionItemTable->getBurnup('ClosedDate'));
        $this->response->setContent(json_encode($burnUp));
        return $this->response;
    }

构建图表代码

for ($y = $minYear; $y <= $maxYear; $y++)
{
        if ($y == $minYear)
            $startMonth = $minMonth;
        else
            $startMonth = 1;

        if ($y == $maxYear)
            $finishMonth = $maxMonth;
        else
            $finishMonth = 12;


        for ($m = $startMonth; $m <= $finishMonth; $m++)
        {                       
            if ($m < 10)
            {
                $month = "0$m";
            }
            else 
            {
                $month = "$m";
            }
            $monthStr = $this->getMonth($m);


            for ($d = 1; $d <= 31; $d++)
            {
                    if ($d< 10)
                    {
                        $day = "0$d";
                    }
                    else 
                    {
                        $day = "$d";
                    }

                    $dt = "$monthStr $day $y";
                    $start = "$y-$month-$day";
                    $end = "$y-$month-$day";

                    $where = $this->filterString();
                    $filtered = "SELECT * FROM actionitems " . $where;

                    if ($field == 'AssignedDate')
                    {
                        array_push($subsel, "(select '$dt' as AssignedDate, sum(case when AssignedDate Between '$start' and '$end' then 1 else 0 end) as 'NumActionItems' from ($filtered) s)");
                    }
                    if ($field == 'ECD')
                    {
                        array_push($subsel, "(select '$dt' as ECD, sum(case when ECD Between '$start' and '$end' then 1 else 0 end) as 'NumActionItems' from ($filtered) s)");
                    }
                    if ($field == 'DueDate')
                    {
                        array_push($subsel, "(select '$dt' as DueDate, sum(case when DueDate Between '$start' and '$end' then 1 else 0 end) as 'NumActionItems' from ($filtered) s)");
                    }
                    if ($field == 'ClosedDate')
                    {
                        array_push($subsel, "(select '$dt' as ClosedDate, sum(case when ClosedDate Between '$start' and '$end' then 1 else 0 end) as 'NumActionItems' from  ($filtered) s)");
                    }
            }
        }
    }

    if (count($subsel) == 0)
        return array();


    $sub = join(" union all ", $subsel);

    if ($field == 'AssignedDate')
    {
        $sql = "select AssignedDate, (@csum:= @csum + NumActionItems) as TotalActionItems from ($sub) t";
    }
    if ($field == 'ECD')
    {
        $sql = "select ECD, NumActionItems as ECDItems, (@csum:= @csum + NumActionItems) as TotalActionItems from ($sub) t";
    }
    if ($field == 'DueDate')
    {
        $sql = "select DueDate, NumActionItems as OriginalDueItems, (@csum:= @csum + NumActionItems) as TotalActionItems from ($sub) t";
    }
    if ($field == 'ClosedDate')
    {
        $sql = "select ClosedDate, NumActionItems as AcutalClosedItems, (@csum:= @csum + NumActionItems) as TotalActionItems from ($sub) t";
    }

生成的购物车(顶部堆积/底部燃尽)

您查询的主要部分是:

SELECT 'Mar 01 2015' AS AssignedDate,
            Sum(CASE
                  WHEN assigneddate BETWEEN '2015-03-01' AND '2015-03-01'
                THEN 1
                  ELSE 0
                end)      AS 'NumActionItems'
     FROM   (SELECT *
             FROM   actionitems) s;

此查询有 1 个缺点:每个日期都会扫描 table 个操作项。

为了改进这一点,查询可以写成:

SELECT 'Mar 01 2015' AS AssignedDate,
            COUNT(*) AS 'NumActionItems'
     FROM actionitems
     WHERE assigneddate BETWEEN '2015-03-01' AND '2015-03-01';

这还不够好,所以需要下一步: 所有日期的替代方案可以是:

SELECT assigneddate,
            COUNT(*) AS 'NumActionItems'
     FROM actionitems
     WHERE assigneddate BETWEEN '2015-03-01' AND '2015-03-30'
     GROUP BY assigndate;

这将为您提供每个日期的 'NumActionItems'。

将您的@csum 添加为外部查询:

SELECT assigneddate,
   ( @csum := @csum + numactionitems ) AS TotalActionItems 
FROM (    SELECT assigneddate,
            COUNT(*) AS 'NumActionItems'
     FROM actionitems
     WHERE assigneddate BETWEEN '2015-03-01' AND '2015-03-30'
     GROUP BY assigndate) a

应该给出相同的结果。

同时添加索引:

CREATE INDEX idx_ai_nn_1 ON actionitems(assigneddate);

如果您扫描 table 的日期范围 << 小于您选择的行数,则此索引将起作用。