我有一个如下所示的查询:
SELECT *
FROM A
INNER JOIN B ON A.AId = B.AId
WHERE A.ADate BETWEEN @Start and @End
or B.BDate BETWEEN @Start and @End
表A和B的大小大致相同,行数很多。执行计划显示索引搜索,但看起来它正在扫描整个索引。
如果我将or
更改为and
,则查询速度非常快。我认为这是因为如果不对两个表执行表扫描来计算or
,就无法知道or
的结果。 and
很容易分成两个操作。
我读过一些人说可以使用UNION
代替or
,但如果OR中的两个条件都为真,这可能会引入重复行。
有什么解决方案可以减少连接的大小并阻止两个表的完全连接?我愿意重构查询但是可以使这个工作成为可能,但是需要查询的逻辑(给我一个项目,其中匹配范围的日期或B中的日期与范围匹配)保持不变。
答案 0 :(得分:2)
如果在使用内联表加入之前对每个表进行预过滤呢?
SELECT A.*, B.*
FROM (SELECT AId AS Id FROM A WHERE A.ADate BETWEEN @Start and @End
UNION
SELECT BId AS Id FROM B WHERE B.BDate BETWEEN @Start and @End) AS FilteredIds
INNER JOIN A ON A.AId = FilteredIds.Id
INNER JOIN B ON B.BId = FilteredIds.Id
答案 1 :(得分:1)
UNION
不会引入重复的行。 UNION ALL
可能会引入重复项。
请参阅http://www.w3schools.com/sql/sql_union.asp
我想象:
SELECT *
FROM A
INNER JOIN B ON A.AId = B.AId
WHERE A.ADate BETWEEN @Start and @End
UNION
SELECT *
FROM A
INNER JOIN B ON A.AId = B.AId
WHERE B.BDate BETWEEN @Start and @End
可能是一个更快的查询。
答案 2 :(得分:0)
感谢您的回答,最后我选择UNION ALL
,并根据两个相互排斥的选择的联合制作了一个查询,因此UNION ALL
中不会引入任何重复项
首先,获取ADate
在范围内的所有行,并排除BDate
在范围内的行。然后获取BDate
在范围内的所有行。这两个集合的联合在逻辑上产生了涵盖ADate
或BDate
的行集,而没有重复计算中间值(因此UNION ALL
不会产生重复项)。如果你看到这个逻辑中存在缺陷,请告诉我,我发现想到维恩图是有帮助的。
这使得查询执行了最好的选项(在我的情况下),并没有过于复杂,所以我选择了它。
SELECT *
FROM A
INNER JOIN B ON A.AId = B.AId
WHERE A.ADate BETWEEN @Start and @End
and B.BDate NOT BETWEEN @Start and @End
UNION ALL
SELECT *
FROM A
INNER JOIN B ON A.AId = B.AId
WHERE B.BDate BETWEEN @Start and @End
在某些情况下,这可能是OR
运算符的查询优化,尤其是在查询单独的大表时,它适用于日期范围,但可以与我想象的任何其他谓词一起使用。