更快地运行SQL查询

时间:2011-01-06 12:03:28

标签: sql sql-server-2000 query-optimization normalization relational-database

   SELECT projectID, urlID, COUNT(1) AS totalClicks, projectPage,
   (SELECT COUNT(1)
     FROM   tblStatSessionRoutes, tblStatSessions
     WHERE  tblStatSessionRoutes.statSessionID = tblStatSessions.ID
     AND    tblStatSessions.projectID = tblAdClicks.projectID
     AND    (tblStatSessionRoutes.leftPageID = tblAdClicks.projectPage OR
           tblStatSessionRoutes.rightPageID = tblAdClicks.projectPage)) AS totalViews
   FROM   tblAdClicks
   WHERE  projectID IN (SELECT projectID FROM tblProjects WHERE userID = 5)
   GROUP  BY projectID, urlID, projectPage
   ORDER  BY CASE projectID
          WHEN 170 THEN
           1
          ELSE
           0
      END, projectID

这绝不是一个特别复杂的查询,但由于数据库被规范化到一个良好的水平,并且我们正在处理大量数据,因此该查询对用户来说可能非常慢。

有没有人有关于如何提高速度的提示?如果我策略性地对数据库的某些部分进行非规范化处理会有帮助吗?在存储过程中运行它会有显着的改进吗?

我处理数据的方式在我的代码中很有效,这个问题的瓶颈确实存在。

谢谢!

4 个答案:

答案 0 :(得分:3)

我会尝试分手

projectID IN (SELECT projectID FROM tblProjects WHERE userID = 5)

并改为使用JOIN:

 SELECT 
     projectID, urlID, COUNT(1) AS totalClicks, projectPage,
     (SELECT COUNT(1) ....) AS totalViews
 FROM
     dbo.tblAdClicks a
 INNER JOIN 
     dbo.tblProjects p ON a.ProjectID = p.ProjectID
 WHERE 
     p.UserID = 5
 GROUP BY 
     a.projectID, a.urlID, a.projectPage
 ORDER BY 
     CASE a.projectID
        WHEN 170 THEN 1
        ELSE 0
     END, a.projectID

不确定这会有多大帮助 - 我希望能有所帮助!

除此之外,我会检查您是否在相关列上有索引,例如在a.ProjectID上(以帮助加入),可能在a.urlIDa.ProjectPage上(以帮助GROUP BY

答案 1 :(得分:3)

取消规范化您的数据库应该是最后的手段,因为(仅选择一个原因)您不希望鼓励去规范化允许的数据不一致。

首先要看看你是否可以从查询执行计划中获得一些线索。例如,可能是您的子选择花费太多,并且最好先进入临时表,然后在主查询中加入。

此外,如果您看到许多表扫描,您可以从改进的索引中受益。

如果您还没有,则应花几分钟时间重新格式化查询以提高可读性。令人惊讶的是,在这样做的过程中,明显的优化会经常出现在你面前。

答案 2 :(得分:1)

如果您的dbms有一个解释其查询计划的工具,请先使用它。 (您的第一个相关子查询可能每行运行一次。)确保WHERE子句中引用的每个列都有索引。

这个子查询 - WHERE projectID IN(SELECT projectID FROM tblProjects WHERE userID = 5) - 肯定会受益于被剪切并作为视图实现。然后加入视图。

将点击流数据视为数据仓库应用程序并不罕见。如果你需要走这条路,我通常会实现一个单独的数据仓库,而不是对设计良好的OLTP数据库进行非规范化。

我怀疑将它作为存储过程运行会对你有帮助。

答案 3 :(得分:1)

我会尝试删除相关子查询(内部(SELECT COUNT(1) ...))。必须加入你的会话路线,其中左页或右页匹配使事情有点棘手。有些事情(但我没有测试过):

SELECT tblAdClicks.projectID, tblAdClicks.urlID, COUNT(1) AS totalClicks, tblAdClicks.projectPage,
       SUM(CASE WHEN leftRoute.statSessionID IS NOT NULL OR rightRoute.statSessionID IS NOT NULL THEN 1 ELSE 0 END) AS totalViews
FROM tblAdClicks
     JOIN tblProjects ON tblProjects.projectID = tblAdClicks.projectID
     LEFT JOIN tblStatSessions ON tblStatSessions.projectID = tblAdClicks.projectID
     LEFT JOIN tblStatSessionRoutes leftRoute ON leftRoute.statSessionID = tblStatSessions.ID AND leftRoute.leftPageID = tblAdClicks.projectPage
     LEFT JOIN tblStatSessionRoutes rightRoute ON rightRoute.statSessionID = tblStatSessions.ID AND rightRoute.rightPageID = tblAdClicks.projectPage
WHERE tblProjects.userID = 5
GROUP BY tblAdClicks.projectID, tblAdClicks.urlID, tblAdClicks.projectPage
ORDER BY CASE tblAdClicks.projectID WHEN 170 THEN 1 ELSE 0 END, tblAdClicks.projectID

如果我要添加一些缓存表来帮助这个,正如我所说,我会尝试将针对左右页面的tblStatSessionRoutes的两个查询减少为单个查询。如果你知道leftPageID永远不会等于rightPageID,那么应该可以简单地使用一个触发器来填充附加表,例如,在左边视图和右视图中使用不同的行。