TL:DR; version:
If I insert a record into a linked table that has a trigger that inserts a record in a different table, Access displays the global i
An ODBC trace reveals that Access is indeed calling SELECT @@IDENTITY
(as opposed to SCOPE_IDENTITY()
) after inserting the row into the SQL Server linked table:
Database1 e00-1490 EXIT SQLExecDirectW with return code 0 (SQL_SUCCESS)
HSTMT 0x00000000004D6990
WCHAR * 0x000000000F314F28 [ -3] "INSERT INTO "dbo"."Table1" ("txt") VALUES (?)\ 0"
SDWORD -3
...
Database1 e00-1490 EXIT SQLExecDirectW with return code 0 (SQL_SUCCESS)
HSTMT 0x00000000004D6990
WCHAR * 0x000007FED7E6EE58 [ -3] "SELECT @@IDENTITY\ 0"
SDWORD -3
Furthermore, this behaviour appears to depend on the ODBC driver being used, since a similar test with MySQL Connector/ODBC shows that Access does not call the corresponding MySQL function LAST_INSERT_ID()
after inserting a row into a MySQL linked table.
Given that Access is calling SELECT @@IDENTITY
, we must modify our trigger as follows (source: here) to reset the @@IDENTITY value back to its original value
create trigger mytable_insert_trigger on mytable for insert as
declare @identity int
declare @strsql varchar(128)
set @identity=@@identity
--your code
--insert into second table ...
--your code
set @strsql='select identity (int, ' + cast(@identity as varchar(10)) + ',1) as id into #tmp'
execute (@strsql)
You must be doing something else on that form.
I would suggest you delete, and re-create the linked table from Access. I am unable to re-create your effect.
Keep in mind that Access does not and will not display the auto number id until you move to a new record, or you hit ctrl-s to save the current record. However, there really no reason to worry or care about this "lack" of PK id on the Access form until such time a save has occurred.
From what I can see is that you linked to the wrong table from Access. So I much suggest you delete the linked table in Access, re-link.
And as a quick test, after you link the table, flip it into design mode to ensure that Access sees the PK (access will not see the PK if this is for example a view – but you “can” select the PK when linking to a view via the GUI).
edit - I run your scripts - played a bit. I set the first table to auto inc at 1000. The screen shot after entering 3 rows is this:
From what I can see, this is correct.
edit#2: From a quick search on the internet - we see that you issue DOES exist, but I am at a loss as to why my example works 100% just fine.
In the end, I've tried numerous workarounds to solve this problem. For anyone encountering it in the future, here are some of the working ones, and my considerations.
At first, I just moved data entry to a form, and added the following code:
Private Sub Form_AfterInsert()
Me.Requery
End Sub
While working, this had numerous disadvantages.
Then, I just incremented the identity seed of my _Changes table to beyond that of the normal table:
DBCC CHECKIDENT ('MyTable_Changes', RESEED, 10000);
This avoids @@IDENTITY
existing in MyTable
, so the wrong data will no longer be displayed after adding a row. This works, but Access will no longer fetch defaults for blank columns after adding. However, for others, this might not be relevant and might be the simplest solution.
I also tried changing the identity column to a GUID
CREATE TABLE MyTable_Changes(
[ID] [int] NOT NULL,
[Col1] [nvarchar](255) NULL,
[Col2] [nvarchar](255) NULL,
[IDChange] uniqueidentifier ROWGUIDCOL PRIMARY KEY NOT NULL
CONSTRAINT [DF_MyTable_Changes_IDChange] DEFAULT newsequentialid()
)
This worked (since @@IDENTITY
no longer got changed), fetching defaults worked, it added some complexity (I was inserting into two tables joined by a one-many relationship and had to use the OUTPUT
clause to fetch the ID on one of them), and my boss decided GUIDs were unintuitive and shouldn't be used.
In the end, Gord Thompson's answer was the one I went with. I modified the code to use a table variable instead of a temporary table to make the scope of the table more explicit.
CREATE TRIGGER MyTableTrigger ON MyTable AFTER Insert, Update
AS
BEGIN
SET NOCOUNT ON;
-- Capture @@identity
DECLARE @identity int;
SET @identity=@@identity;
-- Inserts here
INSERT INTO MyTable_Changes(ID, Col1, Col2)
SELECT * FROM Inserted;
-- reset @@identity
DECLARE @strsql varchar(255)
SET @strsql='
DECLARE @t Table(id INTEGER IDENTITY(' + cast(@identity as varchar(10)) + ',1) PRIMARY KEY);
INSERT INTO @t DEFAULT VALUES;
'
EXEC(@strsql);
END