1

您好,我有一个名为 (Details) 的表,其中包含有关机器的信息(每台机器只有一条记录):

MachineId    PersonId
---------------------
MacA         PersY
MacB         PersX

我有另一个表(呼叫),其中包含维护呼叫:

CallId        MachineId
-----------------------
Call01       MacA
Call02       MacB
Call03       MacA

第三个表(访问)包含响应呼叫的人:

CallId        PersonId       Date
--------------------------------
Call01       PersA          15/05/2013
Call02       PersB          20/05/2013
Call03       PersC          25/08/2013

我需要的是创建一个查询来使用最后一个响应呼叫的人更新详细信息表,在此示例中 MacA 是 PersC 而 MacB 是 PersB

我的问题是我需要在 Access 2003 中执行此操作。我尝试使用 SELECT TOP 1 PersonId ORDER BY Date DESC 进行子查询并创建表之间的关系(它在 sql server 中有效,但不在此处)

谢谢。试过:

UPDATE Details SET Details.PersonId = t.Person 
FROM Details INNER JOIN
(SELECT TOP 1 Call.MachineId, Visit.PersonId AS Person 
 FROM Call INNER JOIN Visit ON Call.CallId = Visit.CallId 
 ORDER BY Visit.Date DESC) AS t ON Details.MachineId = t.MachineId;
4

2 回答 2

2

您可以使用SELECT带有相关子查询的查询来检索PersonId每个MachineId.

SELECT
    d.MachineId,
    (
        SELECT TOP 1 v.PersonId
        FROM
            Visit AS v
            INNER JOIN Calls AS c
            ON v.CallId = c.CallId
        WHERE c.MachineId = d.MachineId
        ORDER BY v.Date DESC
    ) AS person_id
FROM Details AS d;

Visit这种方法的优点是它相当简单,并且只要和/或Calls表中的数据发生变化,结果总是一致的。换句话说,您无需执行 anUPDATE即可查看最新结果。

如果您需要更改 中的存储值Details.PersonId,则不能简单地直接使用相关子查询,因为 Access 会抱怨“操作必须使用可更新查询”。

于 2013-09-02T13:37:20.820 回答
1

MS Access Jet/ACE db 引擎不支持通过子查询进行更新(除其他限制外)。

您有三个主要选择:

选项 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。您需要保存选择查询,因为 stock 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
于 2013-09-02T13:49:20.373 回答