这似乎是一个常见的问题,但是大多数解决方案都是指连接多个SQL命令,我认为这些命令无法用ADO / VBA完成(但我很高兴在这方面显示错误)。< / p>
我当前插入了我的新记录,然后使用(我希望)足够的字段运行一个选择查询,以保证只返回新插入的记录。我的数据库很少被一个人一次访问(在查询之间发生另一次插入的风险可以忽略不计),并且由于表的结构,识别新记录通常很容易。
我现在正在尝试更新一个没有太多唯一性范围的表,而不是人工主键。这意味着新记录可能不是唯一的风险,我不愿意添加字段以强制唯一性。
在这种情况下,将记录插入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
。为了将来参考,虽然我有兴趣看看我上面的尝试有什么问题。
答案 0 :(得分:13)
关于您的问题:
我现在正在尝试更新一个表 没有太大的余地 独特性,除了在 人工主键。这意味着 新记录存在风险 可能不是唯一的,我不喜欢 添加一个字段只是为了强制唯一性。
如果 使用自动增量作为主键,那么您具有唯一性,可以使用SELECT @@Identity;
获取上次自动生成ID的值(请参阅下面的警告)。 / p>
如果您不使用自动增量,并且您要从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,您有几个选择:
使用查询创建数据链接,因此您可以直接在Cell或范围内使用结果。
来自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
时,您必须是careful of the caveats:
它仅适用于AutoIncrement标识字段。
仅在您使用ADO并运行SELECT @@IDENTITY;
返回最新使用的计数器but that's for all tables。你不能用它来返回MS Access中特定表的计数器(据我所知,如果你使用FROM mytable
指定一个表,它就会被忽略)。
简而言之,返回的值可能与您期望的完全不同。
您必须在INSERT
后直接查询,以尽量减少错误答案的风险
这意味着,如果您一次插入数据并需要在其他时间(或其他地方)获取最后一个ID,则无法使用。
最后但并非最不重要的是,只有在通过编程代码插入记录时才设置变量
这意味着通过用户界面添加了记录,@@IDENTITY
将不会被设置。
(*):为了清楚起见,@@IDENTITY
表现不同,并且以更具预测性的方式,如果您对数据库使用ANSI-92 SQL模式。
但问题是ANSI 92的语法略有不同
Access支持的ANSI 89 flavor,用于在Access用作前端时提高与SQL Server的兼容性。
答案 1 :(得分: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")
答案 2 :(得分:3)
Re:“我试图让@@ IDENTITY工作,但这总是使用下面的代码返回0。”
您的代码通过不同的连接对象发送SQL
和SQL2
。我不认为@@identity
将返回除零以外的任何内容,除非您从执行INSERT
语句的同一连接询问。
尝试更改此内容:
myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly
为:
myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly
答案 3 :(得分: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
答案 4 :(得分:0)
这是我的解决方案,不使用@@ 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
享受!
答案 5 :(得分:0)
晚会结束8年......您遇到的问题是您正在使用dbConnectionString创建新连接。 @@ identity特定于您正在使用的连接。
首先,不要关闭原始连接
'.Close
替换
myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly
使用之前用于插入的连接
myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly
你已经全部准备好了。事实上,您甚至不需要指定表格:
SQL2 = "SELECT @@identity AS NewID"