强制 MS Access 将完整查询发送到 SQL 服务器
Posted
技术标签:
【中文标题】强制 MS Access 将完整查询发送到 SQL 服务器【英文标题】:Force MS Access to send full query to SQL server 【发布时间】:2019-11-14 21:26:37 【问题描述】:tl;dr 我正在使用 MS Access 通过 ODBC 从 MS SQL Server DB 查询数据。我使用 Access 编辑器构建了一个查询,它需要很长时间。运行时,CPU、RAM 和 I/O 值极低。如果我单击 Pass-Through 按钮并将查询重写为 SQL,则只需几分钟。在运行 MS SQL 服务器时,会使用大量 CPU、RAM 和 I/O(如所期望的那样)。我的结论是,Access 不会为 MSSQL 服务器提供完整的查询,但可能会自行完成部分。 如何强制 Access 发送完整的查询并让 MSSQL 完成繁重的工作?
加长版 我使用 MS Access 作为前端,通过 ODBC 连接到大型(~100 GB)MS SQL Server DB。我有几个查询真的很慢。我为这个问题选择了一个特定的问题,因为我认为问题在于 Access 和 MSSQL 之间的通信。
Access-specifi-SQL 中的查询如下所示:
SELECT DISTINCTROW dbo_A.IDA, dbo_A.MNr, Max(dbo_APK.G) AS MaxG, dbo_AP.IDAPB
FROM ((dbo_A LEFT JOIN dbo_AP ON dbo_A.IDA = dbo_AP.IDA) LEFT JOIN dbo_APK ON dbo_A.IDA = dbo_APK.IDA) INNER JOIN dbo_L ON dbo_A.IDL = dbo_L.IDL
WHERE (((dbo_L.M) Like [LC Dialog]) AND ((dbo_AP.G)<=[<= G Dialog]))
GROUP BY dbo_A.IDA, dbo_A.MNr, dbo_AP.IDAPB
HAVING (((dbo_AP.IDAPB) Like [IDAPB Dialog]));
如您所见,没有什么太花哨的:三个对话框询问用户用于 WHERE 和 HAVING 子句作为过滤器的值。其余的只是基本命令:SELECT、LEFT/RIGHT/INNER JOIN、WHERE、GROUP BY、HAVING
在运行时,Access 使用 ~ 100MB RAM 和 5% CPU。 MSSQL 的 CPU 占用率约为 10%。两者都几乎没有 I/O。查询需要很长时间。
将其转换为真正的 SQL 归结为将表名中的下划线替换为句点,将参数对话框替换为值并将 DISTINCTROW 更改为 DISTINCT。全部完成。
SELECT DISTINCT dbo.A.IDA, dbo.A.MNr, Max(dbo.APK.G) AS MaxG, dbo.AP.IDAPB
FROM ((dbo.A LEFT JOIN dbo.AP ON dbo.A.IDA = dbo.AP.IDA) LEFT JOIN dbo.APK ON dbo.A.IDA = dbo.APK.IDA) INNER JOIN dbo.L ON dbo.A.IDL = dbo.L.IDL
WHERE (((dbo.L.M) Like 'abc') AND ((dbo.AP.G)<='01.01.2020'))
GROUP BY dbo.A.IDA, dbo.A.MNr, dbo.AP.IDAPB
HAVING (((dbo.AP.IDAPB) Like 1));
这个查询运行得非常快。 MSSQL 使用约 90% 的 CPU,I/O 大致处于底层 SSD 的最大值。
问题是,对于直通查询,我需要将值输入到查询本身并且不能再使用对话框。我能做些什么呢?如何强制 Access 从用户(通过对话框)获取值、构建查询并将所有内容发送到 MSSQL 进行处理?
【问题讨论】:
【参考方案1】:您可以即时修改传递查询。并将您的传递视为一个视图。
例如:
CurrentDb.QueryDefs("YourPassThrough").sql =
"SELECT DISTINCT dbo.A.IDA, dbo.A.MNr, Max(dbo.APK.G) AS MaxG, dbo.AP.IDAPB
FROM ((dbo.A LEFT JOIN dbo.AP ON dbo.A.IDA = dbo.AP.IDA) LEFT JOIN dbo.APK ON dbo.A.IDA = dbo.APK.IDA) INNER JOIN dbo.L ON dbo.A.IDL = dbo.L.IDL
WHERE (((dbo.L.M) Like ' "& abcTextString & "') AND ((dbo.AP.G)<='" & YourDate & "'))
GROUP BY dbo.A.IDA, dbo.A.MNr, dbo.AP.IDAPB
HAVING (((dbo.AP.IDAPB) Like " & YourNumber & "));
那么你可以:
Select * FROM YourPassThrough;
【讨论】:
【参考方案2】:你想看这篇文章:How to optimize Microsoft Access when using ODBC data sources
尤其是从这里开始的部分:
确保将查询发送到服务器进行处理。针对远程数据的查询性能最重要的因素是确保您的服务器运行尽可能多的查询。 Microsoft Jet 数据库引擎尝试将整个查询发送到您的服务器,但在本地计算服务器或您的特定服务器通常不支持的任何查询子句和表达式。服务器不支持的功能一般包括以下:
Access 和 ODBC 驱动程序总是尝试将完整的查询传递给服务器,但有时这是不可能的(或者 Access 认为如此)。
Inner Joins 与多个 Left Joins 的组合在我的经验中通常是一个障碍。
您在 WHERE 和 HAVING 子句中有 dbo_AP
,因此它实际上是一个内部联接 => 您可以将该联接从 LEFT 更改为 INNER。
还可以尝试删除DISTINCTROW
或将其更改为DISTINCT
。 DISTINCTROW
没有等效的 SQL Server,请参阅 here。
这通常是使用旧 Access 版本构建的查询的遗留问题。
但是,如果在不删除所需功能的情况下无法在服务器上处理查询,则必须收集参数,例如在表单上,然后按照 Manuel 的描述构建一个传递查询。
【讨论】:
感谢您的回答。我的收获是:最好将尽可能多的查询传递给 MSSQL。对于这个查询,罪魁祸首是 DISTINCTROW,它实际上是旧 Access 的遗留物。只需将其更改为 DISTINCT 即可使此查询完全在服务器上运行(=fast)。是否有调试模式可以让我查看实际发送到服务器的查询? 酷,我没想到只需删除 DISTINCTROW 就可以做到。查找 ODBC 跟踪:***.com/questions/2732985/logging-odbc-sql-server以上是关于强制 MS Access 将完整查询发送到 SQL 服务器的主要内容,如果未能解决你的问题,请参考以下文章
将 MS SQL Server 查询结果导出到 MS Access
使用 VBA 或 PowerShell 将所有 MS Access SQL 查询导出到文本文件