问题
tl;dr
I am using MS Access to query data from a MS SQL Server DB via ODBC. I built a query using the Access editor and it takes forever. While running, the CPU, RAM and I/O values are extremley low. If I click on the Pass-Through button and rewrite the query to be SQL, then it takes only a few minutes. While running the MS SQL server uses a lot of CPU, RAM and I/O (as desired and expected). My conclusion is, that Access does not provide the MSSQL server with the full query but probably does the parts by itselt. How can I force Access to send the full query and let MSSQL do the heavy lifting?
Long version
I am using MS Access as a frontend to connect via ODBC to a large (~100 GB) MS SQL Server DB. I have several queries which are really slow. I picked a specific one for this question since I believe the problem lies within the communication between Access and MSSQL.
The query in Access-specifi-SQL looks like this:
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]));
As you can see, nothing too fancy: three dialogs asking the user for values which are used for the WHERE and HAVING clauses as filter. The rest is just basic commands: SELECT, LEFT/RIGHT/INNER JOIN, WHERE, GROUP BY, HAVING
While running it, Access uses ~ 100MB RAM and 5 % CPU. MSSQL is at around 10 % CPU. Both do close to no I/O. The query takes forever.
Converting this to real SQL comes down to replacing the underscore from the table name with a period, replacing the parameter dialogs with values and changing DISTINCTROW to DISTINCT. all done.
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));
This query runs very fast. MSSQL uses ~90 % CPU and the I/O is roughly at the maximum of the underlying SSD.
The problem is, that with the pass-through query, I would need to input the values into the query itself and could no longer use the dialogs. What can I do about it? How can I force Access to get the values from the user (with the dialog), build the query and send everything to MSSQL for processing?
回答1:
You can modify your pass-through query on the fly. And treat your pass-through as a view.
For Example:
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 & "));
Then you can just:
Select * FROM YourPassThrough;
回答2:
You want to read this article:
How to optimize Microsoft Access when using ODBC data sources
especially the part starting here:
Make sure queries are sent to the server for processing. The most important factor in query performance against remote data is ensuring that your server runs as much of the query as possible. The Microsoft Jet database engine attempts to send the entire query to your server, but evaluates locally any query clauses and expressions that are not generally supported by servers or by your particular server. Functionality not supported by servers in general includes the following:
Access and the ODBC driver always try to pass the full query to the server, but sometimes it's not possible (or Access thinks so).
The combination of Inner Joins with multiple Left Joins is often a hindrance in my experience.
You have dbo_AP
in the WHERE and HAVING clause, so it's effectively an INNER JOIN anyway => you can change that Join from LEFT to INNER.
Also try removing DISTINCTROW
or changing it to DISTINCT
. DISTINCTROW
doesn't have a SQL Server equivalent, see here.
Often it is a relict in queries built with older Access versions.
But if you can't get the query to be processed on the server without removing functionality you need, you'll have to collect your parameters e.g. on a form, then build a pass-through query as Manuel described.
来源:https://stackoverflow.com/questions/58866769/force-ms-access-to-send-full-query-to-sql-server