我有一个视图,它是联接的集合。为了这个问题的目的,我将把观点简化如下。注意,Table1.Table1ID
是主键,Table2.Table1ID
是外键。
CREATE VIEW [View1] AS
SELECT t1.Column1, t2.Column2
FROM Table1 t1
JOIN Table2 t2 ON t1.Table1ID = t2.Table1ID
为此目的使用视图是很好的,因为它减少了我在代码中必须执行的联接数量,并且允许SQL Server更有效地优化联接。例如:
SELECT Column1, Column2
FROM View1
WHERE Column1 = 'abc'
AND Column2 = 'xyz'
GROUP BY Column1, Column2
SQL Server通过仅连接表1中第1列中的值为"abc"的记录子集和表2中第2列中值为"xyz"的记录子集来优化上述查询。换言之,SQL Server执行计划在应用联接之前将筛选巧妙地应用于视图中的各个表,从而减少了联接中需要考虑的记录数量。
但是,如果我通过将WHERE
子句中的AND
运算符更改为OR
运算符来更改前一个查询,则执行计划在执行联接之前不会应用筛选。
SELECT Column1, Column2
FROM View1
WHERE Column1 = 'abc'
OR Column2 = 'xyz'
GROUP BY Column1, Column2
上面查询的执行计划首先连接表1和表2中的所有记录,然后第二个应用where子句。这符合布尔逻辑,因为OR
运算符测试不能满足,直到表被连接,并且第1列和第2列中的值都存在并被考虑在内。
另一方面,下面的查询返回与前面的查询相同的结果集。
SELECT Column1, Column2
FROM View1
WHERE Column1 = 'abc'
UNION
SELECT Column1, Column2
FROM View1
WHERE Column2 = 'xyz'
后两个查询产生相同的结果,但SQL Server对这两个查询中的第二个进行了优化,以便在视图中应用联接之前,执行计划将where子句应用于相应SELECT
语句中的相应表,从而减少联接的记录。即使视图实际上被调用了两次,并且生成的记录集的交集由UNION
返回,这也会产生更高效的查询。
我的问题是:
- 为什么SQL Server不使用类似于最后一个查询的执行计划来优化倒数第二个查询
- 是否有一种方法可以定义视图,以便SQL Server使用类似于上次查询的执行计划对其进行优化
谢谢。
WHERE ... OR ...
与UNION
不同。如果单个行在Column1
中具有值abc
,在Column2
中具有值xyz
,则在WHERE ... OR ...
的情况下它将仅产生一行,而在UNION
的情况下产生两行。您可能会争辩说UNION
删除了重复项(因为它不是UNION ALL
),我认为这是一个简化的示例,省略了正在投影的其他列。但是,即使考虑到这个示例ad literam,我也可以给出另一种情况,其中两个行对于Column1
具有值abc
,对于Column2
具有值xyz
,并且在这种情况下,第一个查询返回两行,而第二个查询返回1。QED,您所期望的优化无法实现,因为这是不正确的。
为什么SQL Server不使用类似于最后一个查询的执行计划来优化倒数第二个查询?
它可以执行与您提供的示例类似的转换。你需要计算出雷穆斯回答中提到的细节。我确信这是可行的。例如,您可以使用这样一个事实:您可以将UNION
重写为FULL OUTER JOIN
。这使您能够插入正确的重复数据消除条件。
截至2012年,这一转变尚未实施。这很不幸,但产品团队无论如何都无法提供一个完美的优化器。我们的产品非常好。
是否有一种方法可以定义视图,以便SQL Server使用类似于上次查询的执行计划对其进行优化?
据我所知并非如此。
我知道这个答案对你来说并没有改善,但情况就是这样。现在,请考虑创建索引视图。通过这种方式,您可以预先生成感兴趣的行的子集。您可以创建跨多个表的列的索引。可以为表达式编制索引。