【问题标题】:Update Query in MS-Access 2003MS-Access 2003 中的更新查询
【发布时间】:2013-09-05 11:03:01
【问题描述】:

您好,我有一个名为 (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;

【问题讨论】:

  • 发布您尝试使用的完整 sql 语句。
  • 更新详情 S​​ET 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;

标签: sql ms-access ms-access-2003


【解决方案1】:

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

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 表中的数据发生变化,结果总是一致的。换言之,您无需执行UPDATE 即可查看最新结果。

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

【讨论】:

    【解决方案2】:

    MS Access Jet/ACE db 引擎不支持通过子查询进行更新 (among other limitations)。

    您有三个主要选择:

    选项 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 写了一个"extended" DLookup function,允许您指定排序顺序。我进一步稍微修改了函数以允许传入任意 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
    

    【讨论】:

    • 在 HansUp 的回答中,我得到的结果显示了每台机器以及每个对其进行最后一次维护的人。这是第一步,现在我必须找到根据这些结果更新 Calls 表的第二步。在 mwolfe02 的回答中,我总是有一个结果是最后一次维护的人,而不是每台机器。
    • @Lucien:我不小心在上面选项 1 和 2 的源选择查询中留下了 TOP 1 子句。我编辑了我的答案以删除该条款。再试一次,如果您仍然遇到问题,请告诉我。
    猜你喜欢
    • 2010-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多