增加SQL查询性能

时间:2011-07-14 06:24:51

标签: sql-server performance

我有两张桌子。在一个表中,我们输入所有类型的模型,每个模型大约有100行。第二个表包含有关第一个项目的销售数据。我需要产生这样的结果:

Date           Model    Total(WE BOUGHT)   Sold
----------     -----    ----------------   ----
2011-01-21      M34R            300         200
2011-01-21      M71S            250          22

我的查询如下:

select distinct
   CONVERT(varchar(10),x.Scantime,120) as ScanDate,
   x.ModelNumber,
   (  Select count(*)
      from micro_model z
      where
         z.ModelNumber=x.ModelNumber
         and CONVERT(varchar(10),z.scantime,101)
            = CONVERT(varchar(10),x.Scantime,101)
   ) as Total,
   (   select COUNT(*)
       from
          micro_Model m
          inner join micro_model_sold y on m.IDNO=y.IDNO
       where
          CONVERT(varchar(10),m.scantime,101)
             = CONVERT(varchar(10),x.Scantime,101)
          and x.ModelNumber=m.ModelNumber
   ) as Sold
from maxis.dbo.maxis_IMEI_Model x 
where
    CONVERT(varchar(10),x.scantime,101) between '01/01/2011' and '01/25/2011'

我能够从上面的查询中实现这一点,但执行时间超过2分钟。请建议我如何提高性能。我听说过数据透视表和索引视图,但从未做过。

2 个答案:

答案 0 :(得分:6)

您的查询中发生了很多可能导致问题的事情。还有一些不确定的领域应该被解决。对于初学者,请尝试以下查询:

SELECT
   DateAdd(Day, DateDiff(Day, 0, X.ScanTime), 0) ScanDate,
   X.ModelNumber,
   Coalesce(Z.Total, 0) Total,
   Coalesce(Z.Sold, 0) Sold
FROM
   maxis.dbo.maxis_IMEI_Model X
   LEFT JOIN (
      SELECT
         Z.ModelNumber,
         DateAdd(Day, DateDiff(Day, 0, Z.ScanTime), 0) ScanDate,
         Count(DISTINCT M.IDNO) Total,
         Count(Y.IDNO) Sold
      FROM
         micro_model Z
         LEFT JOIN micro_model_sold Y
            ON Z.IDNO = Y.IDNO
      GROUP BY
         DateDiff(Day, 0, Z.ScanTime),
         Z.ModelNumber
   ) Z
      ON X.ModelNumber = Z.ModelNumber
      AND X.ScanTime >= Z.ScanDate
      AND X.ScanTime < Z.ScanDate + 1
WHERE
   X.ScanTime >= '20110101'
   AND X.ScanTime < '20110126'
  1. 转换为字符以进行整个日期比较(通过砍掉代表时间的字符)是非常低效的。最好的做法是按照我在WHERE子句中所示的方式进行操作。请注意,我将最终日期增加了一天,然后使用less-than而不是less-than-or-equal -to(这就是BETWEEN所做的那样)使那个点 exclusive 。所有的连接也需要改变。最后,当 需要删除日期的时间部分时,我在这里显示的DateDiff方法是最好的(有一个稍微快一点的方法更难理解,所以我不推荐它,但是如果您使用的是SQL Server 2008,那么您可以Convert(date, DateColumn)这是最快的。

  2. 使用日期格式'01 / 01/2011'不是区域安全的。如果您的查询曾在语言更改为默认日期格式为DMY的计算机上使用,则您的日期将被错误地解释,交换月份和日期并生成错误。使用格式yyyymmdd是安全的。

  3. 使用相关子查询(括号内的SELECT语句从其他表中提取列值)很不方便,在某些情况下会产生非常糟糕的执行计划。即使优化器通常可以将这些转换为正确的连接,也无法保证。对于查看查询的其他人来说,理解它正在做什么也变得非常困难。如图所示,最好使用外连接来表达这些内容。我将相关子查询转换为派生表。

  4. 使用DISTINCT令人不安。您的查询不应为每个模型返回多行。如果是这样,那么查询中的某些内容在逻辑上是错误的,并且您可能收到的数据不正确。

  5. 认为我已正确地将我的派生表中的两个相关子查询组合在一起。但我没有示例数据和所有架构信息,所以这是我最好的猜测。无论如何,我的查询应该给你一些想法。

  6. 我完全重新格式化了您的查询,因为几乎不可能看到它在做什么。我鼓励您在自己的代码中进行更多格式化。这将帮助您和任何追随您的人更快地了解正在发生的事情。如果您在此站点上询问更多SQL问题,则需要更好地格式化自己的代码。请执行此操作并使用“代码块”按钮或手动将所有代码行缩进4个空格,以便将其作为代码块进行网页格式化。

  7. 你知道,盯着我的查询多一点,很明显我不理解maxis_IMEI_Model和其他表之间的关系。请详细说明表格的含义以及您希望看到的结果。

    我的查询中的问题可以通过简单的GROUP BY解决,并在数字列上抛出一些SUM,但我不是100%肯定。可能是maxis_IMEI_Model表需要完全消失,或者移动到它自己的派生表中,它在被加入之前被单独分组。

答案 1 :(得分:2)

我不是一个SQL专家,但你在那里有一个很多的转换。为什么?为什么在比较它们之前需要将这些日期时间列(这是我假设的scantime等类型)转换为字符串?

我强烈怀疑转换正在消除您从目前所拥有的索引中获得的任何好处。 (你确实有连接中涉及的所有列的索引,对吗?)事实上,你的两个连接看起来像他们应该连接多个列而没有任何where子句...虽然我希望查询优化器如果可能的话,等同地对待它们。

查看每次转换,并检查是否确实需要它。我怀疑你实际上并不需要任何 - 并且最终“之间”甚至可能在此刻做错了,因为你正在转换成一种不可排序的格式。 / p>

一般而言 - 即使只是在SQL中 - 尽可能以自然形式处理数据总是值得的。你正在处理日期/时间 - 那么为什么要将它们作为字符串进行比较呢?转换是性能问题正确性问题的根源。