记录表的查询优化

时间:2012-07-08 14:22:47

标签: mysql sql query-optimization

我无论如何都不是DBA,但我认为我可以使用简单的数据库。但是,这个问题让我很难过。

我有两张桌子。一个在我的网站上保存每个页面的名称。另一个在每次访问页面时保留一条记录。我想加入这两个表来查找每个页面的执行时间,作为平均值和总需求时间,因此我可以确定需要优化工作的位置。这是我的问题:

 SELECT reqtemp.template_path, reqtemp.template_name,
        round(AVG(req.request_execution_time)) AS week_avg_exec_time, COUNT(DISTINCT req.skey) AS week_count, SUM(req.request_execution_time) AS week_total_time,
        round(AVG(req2.request_execution_time)) AS month_avg_exec_time, COUNT(DISTINCT req2.skey) AS month_count, SUM(req2.request_execution_time) AS month_total_time,
        round(AVG(req3.request_execution_time)) AS old_avg_exec_time, COUNT(DISTINCT req3.skey) AS old_count, SUM(req3.request_execution_time) AS old_total_time
 FROM   log_exectime_request_tmp reqtemp
        LEFT JOIN log_exectime_request req ON reqtemp.skey = req.log_exectime_req_tmp_skey
                                          AND req.request_date >= DATE_SUB(Now(), INTERVAL 7 DAY)
        LEFT JOIN log_exectime_request req2 ON reqtemp.skey = req2.log_exectime_req_tmp_skey
                                          AND req2.request_date >= DATE_SUB(Now(), INTERVAL 30 DAY)
        LEFT JOIN log_exectime_request req3 ON reqtemp.skey = req3.log_exectime_req_tmp_skey
                                          AND req3.request_date >= DATE_SUB(Now(), INTERVAL 365 DAY)
                                          AND req3.request_date <= DATE_SUB(Now(), INTERVAL 335 DAY)
 GROUP by reqtemp.template_path, reqtemp.template_name
 ORDER BY week_total_time DESC

根据我的理解,这应该在毫秒内运行,在较大的表中只有2000条记录。至少我希望,因为这最终需要在生产中运行数十万或数百万条记录。相反,我最后一次尝试它花了53分钟。 'Explain'显示它在log_exectime_request_tmp上进行表扫描,这很好,因为该表只有83条记录并且正在使用它们的两个列。其他三个连接使用skey和request_date上的索引,这似乎也是正确的。

有人可以提供一些优化建议吗?为什么这么小的桌子会引起这样的问题呢?我的查询构造不当吗?

表的DDL如下,但我找不到任何方法来附加文件来填充数据(我错过了吗?):

 CREATE TABLE `log_exectime_request` 
 (
    `skey` integer (11) NOT NULL AUTO_INCREMENT , 
    `log_exectime_req_tmp_skey` integer (11), 
    `request_date` datetime, 
    `request_execution_time` integer (11), 
    `query_execution_time` integer (11), 
    `template_execution_time` integer (11), 
    `logging_execution_time` integer (11),
    PRIMARY KEY (`skey`)
 ) TYPE=InnoDB CHARACTER SET latin1 COLLATE latin1_swedish_ci;

 ALTER TABLE `elegantgalleries`.`log_exectime_request` ADD INDEX `date_tempSkey` (`log_exectime_req_tmp_skey`,`request_date` );


 CREATE TABLE `log_exectime_request_tmp` 
 (
    `skey` integer (11) NOT NULL AUTO_INCREMENT , 
    `template_path` varchar (500), 
    `template_name` varchar (250),
    PRIMARY KEY (`skey`)
 ) TYPE=InnoDB CHARACTER SET latin1 COLLATE latin1_swedish_ci;

2 个答案:

答案 0 :(得分:2)

在提出这样的问题时,您应该始终提及您正在使用的数据库。不同的数据库具有非常不同的性能特征。

如果我正确理解了查询,则需要很长时间,因为您不需要进行这么多连接。您似乎正在尝试将请求汇总到三个不同的组中。 。 。上周,上个月和一年前(虽然很奇怪您使用335到365天而不是365天到395)。

我认为您想要的查询看起来更像:

SELECT reqtemp.template_path, reqtemp.template_name,
       round(avg(case when req.request_date >= DATE_SUB(Now(), INTERVAL 7 DAY)
                      then req.request_execution_time
                 end)
            ) as week_avg_exec_time,
       COUNT(DISTINCT case when req.request_date >= DATE_SUB(Now(), INTERVAL 7 DAY)
                           then req.skey
             end) AS week_count,
       SUM(case when req.request_date >= DATE_SUB(Now(), INTERVAL 7 DAY)
                then req.request_execution_time
          ) AS month_total_time,
       round(avg(case when req.request_date >= DATE_SUB(Now(), INTERVAL 30 DAY)
                      then req.request_execution_time
                 end)
            ) as month_avg_exec_time,
       COUNT(DISTINCT case when req.request_date >= DATE_SUB(Now(), INTERVAL 30 DAY)
                           then req.skey
             end) AS week_count,
       SUM(case when req.request_date >= DATE_SUB(Now(), INTERVAL 30 DAY)
                then req.request_execution_time
          ) AS month_total_time,
       round(avg(case when req.request_date >= DATE_SUB(Now(), INTERVAL 365 DAY) AND
                           req.request_date <= DATE_SUB(Now(), INTERVAL 335 DAY)
                      then req.request_execution_time
                 end)
            ) as old_avg_exec_time,
       COUNT(DISTINCT case when req.request_date >= DATE_SUB(Now(), INTERVAL 365 DAY) AND
                                req.request_date <= DATE_SUB(Now(), INTERVAL 335 DAY)
                           then req.skey
             end) AS old_count,
       SUM(case when req.request_date >= DATE_SUB(Now(), INTERVAL 365 DAY) AND
                     req.request_date <= DATE_SUB(Now(), INTERVAL 335 DAY)
                then req.request_execution_time
          ) AS old_total_time
FROM log_exectime_request_tmp reqtemp LEFT JOIN
     log_exectime_request req
     ON reqtemp.skey = req.log_exectime_req_tmp_skey
GROUP by reqtemp.template_path, reqtemp.template_name
ORDER BY week_total_time DESC

您只需加入req表一次即可获取请求日期。然后,您可以使用case语句将时间分成不同的时间段。

您的原始查询在每个时间段的所有记录之间进行交叉连接。虽然在给定的时间内可能只有几百或几千,但当你交叉加入它们时,你会得到数百或数十亿行。

答案 1 :(得分:1)

由于您要对request_date进行过滤,请尝试添加仅包含该列的索引。