MS Access子查询性能

时间:2015-08-18 21:12:19

标签: sql ms-access

我经常使用MS Access作为临时数据处理工具。我注意到的一件事是,以某种方式使用子查询往往会对大表有非常差的性能。

例如,此查询执行效果不佳:

SELECT TOP 500 EmployeeID FROM Employee
WHERE EmployeeID NOT IN
    (SELECT EmployeeID FROM LoadHistory
    WHERE YearWeek = '2015-26');

此版本的同一查询效果很好:

SELECT TOP 500 EmployeeID FROM Employee
WHERE NOT EXISTS
    (SELECT 1 FROM LoadHistory
    WHERE YearWeek = '2015-26' AND
        EmployeeID = Employee.EmployeeID);

同一查询的其他形式也表现良好:

SELECT TOP 500 Employee.EmployeeID
FROM Employee
LEFT JOIN
    (SELECT EmployeeID FROM LoadHistory
    WHERE YearWeek = '2015-26') q
ON Employee.EmployeeID = q.EmployeeID
WHERE q.EmployeeID IS NULL;

由于风格原因,我更喜欢第一种形式。我无法理解为什么优化器不会为第一个和第二个查询生成相同的计划。 ACE优化器在这里的表现有什么逻辑吗?有没有其他方法可以稍微重写第一个查询,以便优化器可以做得更好?

2 个答案:

答案 0 :(得分:2)

NOT INNOT EXISTS非常相似。 。 。但不太一样。

NOT IN的语义指定如果任何值为NULL,它永远不会返回true。这意味着子查询必须验证这是真的。

我的猜测是,这解释了不同的优化方案。这也是我更喜欢NOT EXISTSNOT IN的原因。 NOT EXISTS在处理子查询中的NULL值时更直观。

注意:使用相关子查询时,总是限定列名:

SELECT TOP 500 EmployeeID
FROM Employee
WHERE NOT EXISTS (SELECT 1
                  FROM LoadHistory
                  WHERE LoadHistory.YearWeek = '2015-26' AND
                        LoadHistory.EmployeeID = Employee.EmployeeID
                 );

如果您将LoadHistory.EmployeeId声明为NOT NULL,编译器可能足够聪明以避免这种情况。

我还应该提到NOT EXISTS可以利用LoadHistory(EmployeeId)LoadHistory(EmployeeId, YearWeek)上的索引。 NOT IN版本可以使用LoadHistory(YearWeek)LoadHistory(YearWeek, EmployeeId)。也许你的索引解释了性能上的差异。

答案 1 :(得分:0)

这是inexists之间的差异。 Exists在子查询第一次匹配给定条件时求值为true。另一方面,in将扫描整个表格。