在考虑相关子查询时要小心。它们可能非常慢。如果您构建一个包含两个相关子查询的查询,您将放大问题。
如果您的源表包含少量行(例如几十行),那么速度可能不是问题。但是,如果该表包含一千行,您肯定会注意到它。如果你的JOINT
字段没有被索引,性能可能会非常缓慢。
如果您将从 Access 会话中运行查询,则可以使用域函数 (DMin
和DMax
) 而不是相关子查询。域函数经常被批评为慢。但是,在这种情况下,它们可能比相关子查询快得多。
更正:您无需在 Access 会话中运行查询,即可使用DMin()
andDMax()
函数。我附上了一个 VBScript 示例,它基于我的qryDomainFunctions
. 它可以正常工作并正确报告RecordCount: 1000
我创建了一个表,joints
以长整数字段joint
作为主键并添加了 1000 行。然后我创建了这两个查询:
qryCorrelatedSubqueries:
SELECT
a.joint,
(SELECT TOP 1 joint
FROM joints b
WHERE b.joint>a.joint
ORDER BY joint) AS Ahead,
(SELECT TOP 1 joint
FROM joints b
WHERE b.joint<a.joint
ORDER BY joint DESC) AS Behind
FROM joints AS a;
qryDomainFunctions:
SELECT
j.joint,
DMin("joint","joints","joint > " & [joint]) AS joint_ahead,
DMax("joint","joints","joint < " & [joint]) AS joint_behind
FROM joints AS j;
这是即时窗口的记录,我使用QueryDuration
下面的函数比较了这两个查询的速度。该函数以毫秒为单位返回持续时间。
? QueryDuration("qryDomainFunctions")
0
? QueryDuration("qryCorrelatedSubqueries")
889
请注意,这两个查询都受益于joints
字段上的索引。当我删除索引、压缩数据库并重新运行测试时,我得到了以下结果:
? QueryDuration("qryDomainFunctions")
16
? QueryDuration("qryCorrelatedSubqueries")
4570
这是带有我使用的代码的模块。QueryDuration
绝不是绩效衡量的硬道理。但是,它足以让我们大致了解这两个查询的相对速度。
Option Compare Database
Option Explicit
Private Declare Function apiGetTickCount Lib "kernel32" _
Alias "GetTickCount" () As Long
Public Function QueryDuration(ByVal pQueryName As String) As Long
Dim db As DAO.Database
Dim lngStart As Long
Dim lngDone As Long
Dim rs As DAO.Recordset
Set db = CurrentDb()
lngStart = apiGetTickCount() ' milliseconds '
Set rs = db.OpenRecordset(pQueryName, dbOpenSnapshot)
If Not rs.EOF Then
rs.MoveLast
End If
lngDone = apiGetTickCount()
rs.Close
Set rs = Nothing
Set db = Nothing
QueryDuration = lngDone - lngStart
End Function
DomainFunctionsQuery.vbs:
Option Explicit
Dim cn, rs
Set cn = CreateObject("ADODB.Connection")
cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source='database1.mdb'"
Set rs = CreateObject("ADODB.Recordset")
rs.CursorLocation = 3 ' adUseClient '
rs.Open "qryDomainFunctions", cn, 3 ' adOpenStatic = 3 '
WScript.Echo "RecordCount: " & rs.RecordCount
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing