0

无论如何,我都不是 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 分钟。“解释”显示它在 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;
4

2 回答 2

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 语句将时间分成不同的时期。

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

于 2012-07-08T15:08:24.100 回答
1

由于您在 request_date 上进行过滤,因此请尝试仅使用该列添加索引。

于 2012-07-08T14:45:07.413 回答