我有两张桌子。在一个表中,我们输入所有类型的模型,每个模型大约有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分钟。请建议我如何提高性能。我听说过数据透视表和索引视图,但从未做过。
答案 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'
转换为字符以进行整个日期比较(通过砍掉代表时间的字符)是非常低效的。最好的做法是按照我在WHERE子句中所示的方式进行操作。请注意,我将最终日期增加了一天,然后使用less-than而不是less-than-or-equal -to(这就是BETWEEN所做的那样)使那个点 exclusive 。所有的连接也需要改变。最后,当 需要删除日期的时间部分时,我在这里显示的DateDiff方法是最好的(有一个稍微快一点的方法更难理解,所以我不推荐它,但是如果您使用的是SQL Server 2008,那么您可以Convert(date, DateColumn)
这是最快的。
使用日期格式'01 / 01/2011'不是区域安全的。如果您的查询曾在语言更改为默认日期格式为DMY的计算机上使用,则您的日期将被错误地解释,交换月份和日期并生成错误。使用格式yyyymmdd
是安全的。
使用相关子查询(括号内的SELECT语句从其他表中提取列值)很不方便,在某些情况下会产生非常糟糕的执行计划。即使优化器通常可以将这些转换为正确的连接,也无法保证。对于查看查询的其他人来说,理解它正在做什么也变得非常困难。如图所示,最好使用外连接来表达这些内容。我将相关子查询转换为派生表。
使用DISTINCT
令人不安。您的查询不应为每个模型返回多行。如果是这样,那么查询中的某些内容在逻辑上是错误的,并且您可能收到的数据不正确。
我认为我已正确地将我的派生表中的两个相关子查询组合在一起。但我没有示例数据和所有架构信息,所以这是我最好的猜测。无论如何,我的查询应该给你一些想法。
我完全重新格式化了您的查询,因为几乎不可能看到它在做什么。我鼓励您在自己的代码中进行更多格式化。这将帮助您和任何追随您的人更快地了解正在发生的事情。如果您在此站点上询问更多SQL问题,则需要更好地格式化自己的代码。请执行此操作并使用“代码块”按钮或手动将所有代码行缩进4个空格,以便将其作为代码块进行网页格式化。
你知道,盯着我的查询多一点,很明显我不理解maxis_IMEI_Model
和其他表之间的关系。请详细说明表格的含义以及您希望看到的结果。
我的查询中的问题可以通过简单的GROUP BY
解决,并在数字列上抛出一些SUM
,但我不是100%肯定。可能是maxis_IMEI_Model
表需要完全消失,或者移动到它自己的派生表中,它在被加入之前被单独分组。
答案 1 :(得分:2)
我不是一个SQL专家,但你在那里有一个很多的转换。为什么?为什么在比较它们之前需要将这些日期时间列(这是我假设的scantime
等类型)转换为字符串?
我强烈怀疑转换正在消除您从目前所拥有的索引中获得的任何好处。 (你确实有连接中涉及的所有列的索引,对吗?)事实上,你的两个连接看起来像他们应该连接多个列而没有任何where子句...虽然我希望查询优化器如果可能的话,等同地对待它们。
查看每次转换,并检查是否确实需要它。我怀疑你实际上并不需要任何 - 并且最终“之间”甚至可能在此刻做错了,因为你正在转换成一种不可排序的格式。 / p>
一般而言 - 即使只是在SQL中 - 尽可能以自然形式处理数据总是值得的。你正在处理日期/时间 - 那么为什么要将它们作为字符串进行比较呢?转换是性能问题和正确性问题的根源。