SQL使用OUTER JOIN查询,使用WHERE与AND的区别。
最近在做汇总报表的某部分的时候发现的一个问题。
有下面两段代码看看有什么区别。
--PID流量
--PageID: 首页1,搜索2,登录3,管理4,SC 5。
SELECT
SUM([Flux]) AS [SumPIDFlux],
CONVERT(VARCHAR(10),[PerDayDateTime],120) AS [PerDayDateTime]
FROM
[Sherrys_stat].[dbo].[FluxAreaStats]
RIGHT OUTER JOIN
[#PerDayReport]
ON
CONVERT(VARCHAR(10),[StatDate],120) = CONVERT(VARCHAR(10),[PerDayDateTime],120)
WHERE
[Sherrys_stat].[dbo].[FluxAreaStats].[PageId] = 5
AND
CONVERT(VARCHAR(10),[PerDayDateTime],120) >= CONVERT(VARCHAR(10),@StartDateTime,120)
AND
CONVERT(VARCHAR(10),[PerDayDateTime],120) <= CONVERT(VARCHAR(10),@EndDateTime,120)
GROUP BY
CONVERT(VARCHAR(10),[PerDayDateTime],120)
SELECT
SUM([Flux]) AS [SumPIDFlux],
CONVERT(VARCHAR(10),[PerDayDateTime],120) AS [PerDayDateTime]
FROM
[Sherrys_stat].[dbo].[FluxAreaStats]
RIGHT OUTER JOIN
[#PerDayReport]
ON
CONVERT(VARCHAR(10),[StatDate],120) = CONVERT(VARCHAR(10),[PerDayDateTime],120)
AND
[Sherrys_stat].[dbo].[FluxAreaStats].[PageId] = 5
AND
CONVERT(VARCHAR(10),[PerDayDateTime],120) >= CONVERT(VARCHAR(10),@StartDateTime,120)
AND
CONVERT(VARCHAR(10),[PerDayDateTime],120) <= CONVERT(VARCHAR(10),@EndDateTime,120)
GROUP BY
CONVERT(VARCHAR(10),[PerDayDateTime],120)
上面是一个查询里面的一小部分代码。
其中临时表[#PerDayReport] 的数据格式为:
主要是为了生成一个不间断的时间表,与查询的时间做一个外连来保证时间的连续性。
仔细看一下区别就在于,外连之后ON条件后面一个是AND而一个是WHERE的,执行的时间范围是 2007-01-01 到 2007-1-24号
通过第一个查询得到的结果不是想要的,结果如下:
结果没有1号,仔细一找也没有13,14号的内容,但是我临时表里面时间是连续的,而且也是与它做了外连的查询?为什么还是没有么?
想了一下查询的运行流程才知道,在做外连查询的时候ON里面的条件是在外连执行之前筛选的的,然后再做外联查询,所以里面的条件不对外联的行数产生影响。
而是对查询的结果进行条件限制。所以第一个查询的ON条件只有一个就是让两个表的时间做一个相等的连接,而之后的WHERE条件就是在外联执行之后再进行筛选。
详细分析第一个查询 在外形之后形成的表的结果为(这个结果为一个中间结果 我把它一步步拿出来做分析用 下面称呼此表就为中间表):
然后进行WHERE条件:
CONVERT(VARCHAR(10),[PerDayDateTime],120) >= CONVERT(VARCHAR(10),@StartDateTime,120)
AND
CONVERT(VARCHAR(10),[PerDayDateTime],120) <= CONVERT(VARCHAR(10),@EndDateTime,120)
对于时间条件来说是在没有问题,因为在这个中间表里面的,时间条件之后结果还是一样。
剩下还有一个条件为 [Sherrys_stat].[dbo].[FluxAreaStats].[PageId] = 5
你会发现这个条件并不是在这个中间表里面存在的,那么它是怎样进行筛选的呢。
所以在这个时候它会找到原表的内容进行这个筛选工作,所以已经没有对中间表进行筛选了,因为这个条件不能在中间表里面执行。
而原表里面并不存在中间表的全部日期,当然筛选查询出来的结果没有的最终的结果也会没有了。
因此要做到合理的查询最终应该这样写:
SELECT
SUM([Flux]) AS [SumPIDFlux],
CONVERT(VARCHAR(10),[PerDayDateTime],120) AS [PerDayDateTime]
FROM
[Sherrys_stat].[dbo].[FluxAreaStats]
RIGHT OUTER JOIN
[#PerDayReport]
ON
CONVERT(VARCHAR(10),[StatDate],120) = CONVERT(VARCHAR(10),[PerDayDateTime],120)
AND
[Sherrys_stat].[dbo].[FluxAreaStats].[PageId] = 5
WHERE
CONVERT(VARCHAR(10),[PerDayDateTime],120) >= CONVERT(VARCHAR(10),@StartDateTime,120)
AND
CONVERT(VARCHAR(10),[PerDayDateTime],120) <= CONVERT(VARCHAR(10),@EndDateTime,120)
GROUP BY
CONVERT(VARCHAR(10),[PerDayDateTime],120)
---------------------------------------------------------------------------------------------------------------------------------
[Sherrys_stat].[dbo].[FluxAreaStats].[PageId] = 5
是需要在外联之前做的筛选工作,所以放到ON的筛选里面,
而
CONVERT(VARCHAR(10),[PerDayDateTime],120) >= CONVERT(VARCHAR(10),@StartDateTime,120)AND
CONVERT(VARCHAR(10),[PerDayDateTime],120) <= CONVERT(VARCHAR(10),@EndDateTime,120)
是在外联之后需要做的一个时间范围的筛选这个条件是在上面所说的中间表里面存在的,所以放到WHERE条件里面,
这样语气清晰起来了,查询的数据也不会出问题了。