如何让 MS-Access 为我的查询选择不同/正确的执行计划

Posted

技术标签:

【中文标题】如何让 MS-Access 为我的查询选择不同/正确的执行计划【英文标题】:How can I make MS-Access choose a different/the right execution plan for my query 【发布时间】:2012-01-27 17:14:50 【问题描述】:

我有一个相对简单的查询和 Access 为其选择的执行计划的问题。

查询是这种形式

SELECT somethings
FROM A INNER JOIN (B INNER JOIN (C INNER JOIN D ON ...) ON ...) ON ...
WHERE A.primaryKey= 1 AND D.d = 2;

C 和 D 的行数相对较少。 A 和 B 有几千行。

返回 2 行的查询(不确定这是否相关)真的很慢。它在 17 秒内运行。如果我删除 where 子句的 AND D.d = 2 部分,查询现在返回 4 行并立即运行。

所以我的理解是,JET 引擎可以立即在 D.d 上运行没有过滤器的查询,然后立即执行所述过滤器(仅过滤 4 行)。因此,使用D.d = 2 过滤器运行查询的时间不会太长。

我尝试创建一个不带过滤器的子查询,并将其包含在另一个仅过滤结果的查询中,但它仍然很慢。我的猜测是 JET 引擎足够聪明,可以“扁平化”子查询,所以结果是一样的。

因为我无法按照我的意愿运行查询,所以我使用了 JETSHOWPLAN 东西,以便 Access 输出它的执行计划。这是我发现的:

对于快速查询(没有D.d = 2 的查询),查询计划的第一步是对A 表应用A.primaryKey = 1 过滤器。这导致超过 30000 行中的 1 行的数据集。然后连接似乎是使用索引从 A 到 D 执行的,数据集从不超过 4 行。

慢查询似乎以相反的顺序执行。首先连接 D 和 C,然后测试 D.d = 2。之后,执行从 C 到 A 的连接。通过这种方式,需要从 D 连接到 C、从 C 连接到 B 以及从 B 连接到 A 的数据要大得多。当所有 JOIN 都执行完毕,A.primaryKey=1 执行之前,数据集将有 120K 行。

有没有办法在 Access 上强制执行正确的查询计划?

我希望我很清楚。让我知道是否应该发布查询计划。我没有,因为它们很大。

提前致谢,

mp

【问题讨论】:

由于您无法向查询规划器提供提示,我怀疑您是 SOL。如果这对性能至关重要,您可以将查询的快速部分附加到临时表中,并将其用于另一个 D.d = 2 查询。我知道这听起来很糟糕(确实如此!),但我不知道除了忍受你现在的单个查询的缓慢之外你还能做什么。 @HansUp:感谢您的意见。我担心我将不得不使用如此丑陋的黑客,但如果我找不到任何其他解决方案,我将不得不使用一个。我的用户每天都在等待这个查询的结果几次,而 17 秒是很长的时间,而你所做的只是盯着屏幕。 【参考方案1】:

在 VBA 代码中执行此操作?想法是取出慢的部分并执行快速返回的查询,然后将慢的部分附加到sql中。

db.execute "select * from qryFast inner join d on qryfast.dkey = d.d where d.d = 2

不,模块中的 VBA 代码与子查询不同。 @HansUp 已向我们说明,如上所示,一步执行代码不会提高性能。如果您熟悉在模块中编写代码,您应该能够在内存中快速获得结果,但是在需要的地方获取输出可能会更慢。


也就是说,你应该可以快速将qryFast的结果放入内存中的记录集,然后在qryFast.dkey = d上应用过滤器,也可以快速从'select * from tableD where d中获取记录集=2' 从 tableD 中查找您想要的相关信息,但是将所有这些内容从内存中取出并放到您的前端可以访问的地方可能需要比他们现在等待的 17 秒更长的时间。


事实上,如果您将 qryFast 更改为包含 dkey = 2 的条件(或任何 pk 在 tableD 上的条件),它可能会被踢得够呛


另一个想法:有 3 个查询,qryFast、qryD 和 qryFastWithD 加入两者。我只是在这里抛出一些想法。


或者,正如您在 cmets 中所说,尝试在子查询中包含查询的不同部分,但我认为优化器不会被这种技巧所愚弄,如果将其中的一部分移动到子查询中查询无效。 无论如何,无论什么有效,都接受它。

【讨论】:

不,没试过,只是猜测优化器可能不会扩展到 VBA。他可以尝试在 VBA 中将其分解为多个步骤,而不是像我展示的那样在一个 SQL 语句中完成。 除非我错过了什么,否则我已经尝试过。我创建了一个子查询并将其用作另一个查询的来源,该查询只需 SELECT * FROM qryFast WHERE d = 2。 @Beth:所有 SQL 查询,无论它们来自存储查询还是在 VBA 中生成,仍然由 JET 引擎执行......我想。对吧……? VBA 会将 SQL 语句交给数据库引擎。引擎将使用它的查询计划器来做它的事情。无处可逃! :-) 你能重述最后一个吗,贝丝?我很感兴趣,但它并没有陷入困境。而且我的头开始疼了。【参考方案2】:

在查询规划人员同意我的意见之前,我终于通过混合操作来实现它。我在子查询中隔离了“A.primaryKey= 1”,以确保它在 A 加入 B 之前执行。它是这样的:

SELECT ... 
FROM (SELECT ... FROM A WHERE a.primaryKey=1) AS qryA
   INNER JOIN B ...
WHERE D.d = 2;

【讨论】:

以上是关于如何让 MS-Access 为我的查询选择不同/正确的执行计划的主要内容,如果未能解决你的问题,请参考以下文章

如何将 MS-Access 选择查询的结果保存在 C#.net 中的变量中?

如何使用 MYSQL 查询访问 Ms-Access 数据库

查询不使用 MS-Access 中的日期条件进行拉取

查询未在MS-Access中使用日期条件

如何使用sql语句和vba将数据从MS-Access导入excel power查询?

在列表框 ms-access 2013 VBA 中将多个不同的字段作为列表项返回