MS Access Jet/ACE db エンジンは、サブクエリを介した UPDATE をサポートしていません (他の制限の中でも特に)。
次の 3 つの主要なオプションがあります。
オプション 1: 一時テーブル
最初に「テーブルの作成」クエリとしてサブクエリを実行します。
SELECT Call.MachineId, Visit.PersonId AS Person INTO temp_T
FROM Call INNER JOIN Visit ON Call.CallId = Visit.CallId
ORDER BY Visit.Date DESC
次に、結果の一時テーブルを使用して更新を行います。
UPDATE Details SET Details.PersonId = t.Person
FROM Details INNER JOIN temp_T AS t ON Details.MachineId = t.MachineId
オプション 2: クエリとドメイン集計関数を選択する
選択クエリを作成して保存し、UPDATE クエリで値を設定するときに DLookup を使用します。ストック DLookup 関数では並べ替え順序を指定できないため、選択クエリを保存する必要があります。
SELECT Call.MachineId, Visit.PersonId
FROM Call INNER JOIN Visit ON Call.CallId = Visit.CallId
ORDER BY Visit.Date DESC
上記を LastCalledTech という名前のクエリに保存します。次に、UPDATE クエリを次のように変更します。
UPDATE Details SET Details.PersonId =
DLookup("PersonID", "LastCalledTech", "MachineID=" & Details.MachineID)
オプション 3: 変更された DLookup() カスタム関数
Allen Browne は、ソート順を指定できる「拡張」DLookup 関数を作成しました。さらに関数を少し変更して、(テーブルやクエリ名だけでなく) 任意の SELECT ステートメントを渡すことができるようにしました。この場合、sort パラメータは実際には必要ありません。単純に SELECT SQL 文字列に含める方が効率的だからです。この関数 (以下に投稿しました) を使用して、次の UPDATE クエリを実行します。
UPDATE Details SET Details.PersonId =
ELookup("PersonID", "SELECT Call.MachineId, Visit.PersonId
FROM Call INNER JOIN Visit ON
Call.CallId = Visit.CallId
ORDER BY Visit.Date DESC",
"MachineID=" & Details.MachineID)
そして、ここに変更された関数があります:
Public Function ELookup(Expr As String, Domain As String, Optional Criteria As Variant, _
Optional OrderClause As Variant) As Variant
On Error GoTo Err_ELookup
'Purpose: Faster and more flexible replacement for DLookup()
'Arguments: Same as DLookup, with additional Order By option.
'Return: Value of the Expr if found, else Null.
' Delimited list for multi-value field.
'Author: Allen Browne. allen@allenbrowne.com
'Updated: December 2006, to handle multi-value fields (Access 2007 and later.)
' {by mwolfe02} Add parentheses to allow passing arbitrary SELECT statements
'Examples:
' 1. To find the last value, include DESC in the OrderClause, e.g.:
' ELookup("[Surname] & [FirstName]", "tblClient", , "ClientID DESC")
' 2. To find the lowest non-null value of a field, use the Criteria, e.g.:
' ELookup("ClientID", "tblClient", "Surname Is Not Null" , "Surname")
'Note: Requires a reference to the DAO library.
Dim db As DAO.Database 'This database.
Dim rs As DAO.Recordset 'To retrieve the value to find.
Dim rsMVF As DAO.Recordset 'Child recordset to use for multi-value fields.
Dim varResult As Variant 'Return value for function.
Dim strSql As String 'SQL statement.
Dim strOut As String 'Output string to build up (multi-value field.)
Dim lngLen As Long 'Length of string.
Const strcSep = "," 'Separator between items in multi-value list.
'Initialize to null.
varResult = Null
'Build the SQL string.
strSql = "SELECT TOP 1 " & Expr & " FROM (" & Domain & ")"
If Not IsMissing(Criteria) Then
strSql = strSql & " WHERE " & Criteria
End If
If Not IsMissing(OrderClause) Then
strSql = strSql & " ORDER BY " & OrderClause
End If
strSql = strSql & ";"
'Lookup the value.
Set db = DBEngine(0)(0)
Set rs = db.OpenRecordset(strSql, dbOpenForwardOnly)
If rs.RecordCount > 0 Then
'Will be an object if multi-value field.
If VarType(rs(0)) = vbObject Then
Set rsMVF = rs(0).Value
Do While Not rsMVF.EOF
If rs(0).Type = 101 Then 'dbAttachment
strOut = strOut & rsMVF!FileName & strcSep
Else
strOut = strOut & rsMVF![Value].Value & strcSep
End If
rsMVF.MoveNext
Loop
'Remove trailing separator.
lngLen = Len(strOut) - Len(strcSep)
If lngLen > 0& Then
varResult = Left(strOut, lngLen)
End If
Set rsMVF = Nothing
Else
'Not a multi-value field: just return the value.
varResult = rs(0)
End If
End If
rs.Close
'Assign the return value.
ELookup = varResult
Exit_ELookup:
Set rs = Nothing
Set db = Nothing
Exit Function
Err_ELookup:
MsgBox Err.Description, vbExclamation, "ELookup Error " & Err.number
Resume Exit_ELookup
End Function