9

这似乎是一个很常见的问题,但大多数解决方案都是指连接多个 SQL 命令,我认为 ADO/VBA 无法做到这一点(不过,我很高兴在这方面被证明是错误的)。

我目前插入我的新记录,然后使用(我希望)足够的字段运行选择查询,以保证只能返回新插入的记录。我的数据库一次很少被超过一个人访问(在查询之间发生另一个插入的风险可以忽略不计),并且由于表的结构,识别新记录通常很容易。

我现在正在尝试更新一个没有太多唯一性范围的表,除了人工主键。这意味着存在新记录可能不是唯一的风险,我不愿意添加一个字段只是为了强制唯一性。

在这种情况下,将记录插入 Access 表然后从 Excel 查询新主键的最佳方法是什么?

感谢您的回复。我试图开始@@IDENTITY工作,但这总是使用下面的代码返回 0。

Private Sub getIdentityTest()
    Dim myRecordset As New ADODB.Recordset
    Dim SQL As String, SQL2 As String

    SQL = "INSERT INTO tblTasks (discipline,task,owner,unit,minutes) VALUES (""testDisc3-3"",""testTask"",""testOwner"",""testUnit"",1);"
    SQL2 = "SELECT @@identity AS NewID FROM tblTasks;"

    If databaseConnection Is Nothing Then
        createDBConnection
    End If

    With databaseConnection
        .Open dbConnectionString
        .Execute (SQL)
        .Close
    End With

    myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly

    Debug.Print myRecordset.Fields("NewID")

    myRecordset.Close

    Set myRecordset = Nothing
End Sub

有什么突出的负责吗?

然而,鉴于 Renaud 提供的有用的警告(如下),使用的风险似乎几乎与使用@@IDENTITY任何其他方法一样大,所以我SELECT MAX现在只好使用了。以供将来参考,尽管我很想看看我上面的尝试有什么问题。

4

6 回答 6

14

关于你的问题:

我现在正在尝试更新一个没有太多唯一性范围的表,除了人工主键。这意味着存在新记录可能不是唯一的风险,我不愿意添加一个字段只是为了强制唯一性。

如果您使用AutoIncrement 作为主键,那么您就具有唯一性,您可以使用它SELECT @@Identity;来获取最后一个自动生成的 ID 的值(请参阅下面的注意事项)。

如果您没有使用自动增量,并且您正在从 Access 中插入记录,但您想从 Excel 中检索最后一条:

  • 确保您的主键是可排序的,因此您可以使用以下任一查询来获取最后一个:

    SELECT MAX(MyPrimaryField) FROM MyTable;
    SELECT TOP 1 MyPrimaryField FROM MyTable ORDER BY MyPrimaryField DESC;
    
  • 或者,如果对您的主字段进行排序不会给您最后一个,您将需要添加一个 DateTime 字段(例如InsertedDate)并在每次在该表中创建新记录时保存当前日期和时间,以便您可以获得最后一个像这样的一个:

    SELECT TOP 1 MyPrimaryField FROM MyTable ORDER BY InsertedDate DESC;
    

在这两种情况下,我认为您会发现添加 AutoIncrement 主键更容易处理:

  • 它不会花费你太多

  • 这将保证您记录的唯一性,而无需考虑它

  • 这将使您更容易选择最近的记录,无论是使用@@Identity主键还是通过主键排序或获取Max().

从 Excel

要将数据导入 Excel,您有两种选择:

  • 使用查询创建数据链接,因此您可以直接在单元格或范围中使用结果。

  • 来自 VBA 的查询:

    Sub GetLastPrimaryKey(PrimaryField as string, Table as string) as variant
        Dim con As String
        Dim rs As ADODB.Recordset
        Dim sql As String
        con = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
              "Data Source= ; C:\myDatabase.accdb"
        sql = "SELECT MAX([" & PrimaryField & "]) FROM [" & MyTable & "];"
        Set rs = New ADODB.Recordset
        rs.Open sql, con, adOpenStatic, adLockReadOnly
        GetLastPrimaryKey = rs.Fields(0).Value
        rs.Close
        Set rs = Nothing
    End Sub
    

注意事项@@Identity

在标准 Access 数据库中使用时必须小心注意事项@@Identity(*):

  • 它仅适用于 AutoIncrement Identity 字段。

  • 仅当您使用 ADO 并运行时才可用SELECT @@IDENTITY;

  • 它返回最新使用的计数器,但这是针对所有表的。您不能使用它来返回 MS Access 中特定表的计数器(据我所知,如果您使用 指定表FROM mytable,它只会被忽略)。
    简而言之,返回的值可能根本不是您期望的值。

  • 您必须在 an 之后直接查询,INSERT以尽量减少得到错误答案的风险。
    这意味着如果您一次插入数据并且需要在另一个时间(或另一个地方)获取最后一个 ID,它将无法正常工作。

  • 最后但同样重要的是,仅当通过编程代码插入记录时才设置变量。
    这意味着是通过用户界面添加的记录,@@IDENTITY不会被设置。

(*):为了清楚@@IDENTITY起见,如果您对数据库使用 ANSI-92 SQL 模式,则行为会有所不同,并且以更具预测性的方式。
但问题是 ANSI 92 的语法与 Access 支持的 ANSI 89 风格略有不同,并且当 Access 用作前端时,它旨在提高与 SQL Server 的兼容性。

于 2009-02-28T03:16:22.357 回答
7

如果人工密钥是自动编号,您可以使用@@identity。

请注意,对于这两个示例,事务与其他事件是隔离的,因此返回的身份是刚刚插入的身份。您可以通过在 Debug.Print db.RecordsAffected 或 Debug.Print lngRecs 处暂停代码并将记录手动插入 Table1 来测试这一点,继续代码并注意返回的标识不是手动插入的记录,而是前一个通过代码插入的记录。

DAO 示例

'Reference: Microsoft DAO 3.6 Object Library '
Dim db As DAO.Database
Dim rs As DAO.Recordset

Set db = CurrentDb

db.Execute ("INSERT INTO table1 (field1, Crdate ) " _
            & "VALUES ( 46, #" & Format(Date, "yyyy/mm/dd") & "#)")
Debug.Print db.RecordsAffected
Set rs = db.OpenRecordset("SELECT @@identity AS NewID FROM table1")
Debug.Print rs.Fields("NewID")

ADO 示例

Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset

Set cn = CurrentProject.Connection

cn.Execute ("INSERT INTO table1 (field1, Crdate ) " _
            & "VALUES ( 46, #" & Format(Date, "yyyy/mm/dd") & "#)"), lngRecs
Debug.Print lngRecs
rs.Open "SELECT @@identity AS NewID FROM table1", cn
Debug.Print rs.Fields("NewID")
于 2009-02-27T15:42:12.620 回答
3

回复:“我试图让@@IDENTITY 工作,但这总是使用下面的代码返回 0。”

您的代码发送SQLSQL2通过不同的连接对象。@@identity除非您从执行INSERT语句 的同一连接中询问,否则我认为不会返回零以外的任何内容。

尝试改变这个:

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly

到:

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly
于 2009-03-12T17:16:38.497 回答
1

这是我不使用@@index 或MAX 的解决方案。

Const connectionString = "Provider=SQLOLEDB; Data Source=SomeSource; Initial Catalog=SomeDB; User Id=YouIDHere; Password=YourPassword"
Const RecordsSQL = "SELECT * FROM ThatOneTable"

Private Sub InsertRecordAndGetID()
    Set connection = New ADODB.connection
    connection.connectionString = connectionString
    connection.Open
    Set recordset = New ADODB.recordset
    recordset.Open SQL, connection, adOpenKeyset, adLockOptimistic

    With recordset
        .AddNew
        !Field1 = Value1
        !Field2 = Value2
    End With

    recordset.MoveLast
    ID = recordset.Fields("id")

End Sub

享受!

于 2012-01-27T23:27:24.730 回答
0

试试下面的宏代码。首先从控制框中添加一个命令按钮到工作表,然后在代码窗口中粘贴以下代码

Private Sub CommandButton1_Click()
    MsgBox GetLastPrimaryKey
End Sub

Private Function GetLastPrimaryKey() As String
Dim con As String
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sql As String
con = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\myaccess.mdb;Persist Security Info=False"
sql = "SELECT MAX(id) FROM  tblMyTable"

Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset
cn.Open con
rs.Open sql, cn, 3, 3, 1
If rs.RecordCount <> 0 Then
   GetLastPrimaryKey = rs.Fields(0).Value
End If
rs.Close
cn.Close
Set rs = Nothing
Set cn = Nothing
End Function
于 2009-03-01T06:29:36.300 回答
0

派对迟到了 8 年......您遇到的问题是您正在使用 dbConnectionString 创建一个连接。@@identity 特定于您正在使用的连接。

一、不要关闭原来的连接

'.Close

代替

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly

使用您之前用于插入的连接

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly

你已经准备好了。实际上,您甚至不需要指定表:

SQL2 = "SELECT @@identity AS NewID"
于 2017-05-09T21:43:56.623 回答