Reference a column and update it in the same statement - sql

I have a table that I need to move a value from one column to another then set a new value in the original column.
declare #foo table (A int, B int);
insert into #Foo select 1, 0;
update #Foo
set B = A,
A = 2;
After the update does B always contain 1 or is this non deterministic behavior and it will sometimes have a value of 2 due to A being updated first (and all my tests have just never hit just right the conditions to have it be 2)?
As a followup question if the answer is "B will always be 1", if I do the following will I still get the same result?
update #Foo
set A = 2,
B = A;

When doing an update, SQL Server keeps two versions of the row. The old one (aka deleted) and the new one (aka inserted.) The assignment itself only modifies the inserted row. All assignments use the same variable values from the deleted row. The order of the assignment does not matter.
You can visualize this using the output clause:
declare #t table (a int, b int)
insert #t values (1,2), (2,3)
update #t
set b=a
, a=b
output inserted.*
, deleted.*
select * from #t

B will always contain 1 and the order of the clauses makes no difference. Conceptually the operations happen "all at once".
The following also works in SQL to swap two values without requiring any intermediate variable.
update #Foo
set A = B,
B = A;

Related

How to loop with a table values when column value is not incremental in SQL Server

I need to insert the value from the table T1 to another table t2 where t1 is truncate and load and any values can come after load. So how to use Loop to insert data into T2. It should happen automatically no manual intervention should required so can't Use table value parameter.
Suppose table1 has column Id
Id
---
4
7
15
I have to insert the data into table 2.
I have used this code:
DECLARE #counter INT = (SELECT MIN(CAST(ID AS INT)) FROM Table1);
WHILE #counter <= (SELECT COUNT(CAST(ID AS INT)) FROM Table1)
BEGIN
INSERT INTO TABLE2 (ID)
VALUES (#Counter)
SET #counter = (SELECT ID FROM table1 WHERE #counter = ID)
END
How to set the counter or pick the value from table1.Id value can come differently every time?
Please help
In the absence of any further detail, it seems you could simply rewrite your query as the below:
INSERT INTO Table2 (ID)
SELECT ID
FROM Table1;
There is no need for a loop (WHILE/CURSOR) for what you have here. SQL is a Query Language, and excels are set based operations. What SQL isn't good at is iterative ones, and whenever a CURSOR or WHILE is used, I would suggest it is almost always being misused; this certainly appears to be one of those times. A WHILE or CURSOR, for a even slightly larger dataset would be significantly slower, probably by 1,000s of times so, than the simple statement above.
Not sure of your logic and its almost always better to use set based solutions but here is a TSQL loop solution. I left out the casting which you may have to use:
declare #curid int
declare #previd int
select #curid= min([ID]) from Table1 ;
while ##rowcount > 0
begin
INSERT INTO TABLE2 (ID) VALUES (#curid)
set #previd=#curid
select #curid= min([ID])
from Table1
where [ID]> #previd;
end

How to maintain transaction integrity per common column value for inserting into two tables?

Let's say I have two table variables declared as below:
DECLARE #Table1 TABLE (
A INT,
B NVARCHAR(100)
)
DECLARE #Table2 TABLE (
A INT,
C NVARCHAR(100)
)
Here are the contents of #Table1:
1, 'Hello'
2, 'Hi'
3, 'Ola'
These are the contents of #Table2:
1, 'my old friend'
1, 'sweetheart'
2, 'buddy'
4, 'the end'
Now I want to insert #Table1 into a table X and #Table2 into a table Y. The scenario is that I have to maintain transaction integrity for the insertion into both X and Y for every same value of column A.
For instance, let's say I am inserting the first row (1,'Hello') of #Table1 into X. This means I must also insert the first two rows ((1,'my old friend'), (1,'sweetheart')) of #Table2 into Y in the same transaction. So if any insert of Y fails for A=1, X also fails for A=1. For any value of column A that is not in both #Table1 and #Table2, they are individual transactions by themselves (e.g. A=3 in #Table1 and A=4 in #Table2).
Here are the ways I see to deal with this problem:
I fetch all values of A in both #Table1 and #Table2, run a cursor over it and then for each value of A, I insert into tables X and Y in a single transaction. The issue here is first of all, I don't want to use cursors as much as possible and also, this would mean a super large number of individual inserts.
I pre-validate my #Table1 and #Table2 values and then do one single insert of #Table1 on X and #Table2 on Y. This will be much faster than the above method. But the issues I see here are that first of all, not putting it in a 'transaction' somehow doesn't seem right and also, there could be a small chance I might have missed a validation somewhere (unlikely, yet still).
Which approach should I go for? Is there a better solution?
P.S. Please also note that I do not want to fail the entire insert on X and Y if there is an issue in inserting for only one or few values of A. Also, going back and deleting tables from my DB based on the failed inserts is also not an option as it messes with the running id continuity which I am trying to avoid.
A DML statement is executed completely or not executed at all.
You can do a mix of your two options
First add as much validations as possible, if it fails run the second one using a temp table instead of a cursor
BEGIN TRY
BEGIN TRAN Opt2
--Option 2
COMMIT TRAN Opt2
END TRY
BEGIN CATCH
ROLLBACK TRAN Opt2
DECLARE #A INT, #B VARCHAR(100)
SELECT * INTO #TMP FROM #Table1
WHILE EXISTS (SELECT 1 FROM #TMP)
BEGIN
SELECT TOP 1 #A = A, #B = B FROM #Table1
-- Option 1
DELETE #Temp WHERE A = #A
END
END CATCH

Does anyone know a neat trick for reusing identity values?

Typically when you specify an identity column you get a convenient interface in SQL Server for asking for particular row.
SELECT * FROM $IDENTITY = #pID
You don't really need to concern yourself with the name if the identity column because there can only be one.
But what if I have a table which mostly consists of temporary data. Lots of inserts and lots of deletes. Is there a simple way for me to reuse the identity values.
Preferably I would want to be able to write a function that would return say NEXT_SMALLEST($IDENTITY) as next identity value and do so in a fail-safe manner.
Basically find the smallest value that's not in use. That's not entirely trivial to do, but what I want is to be able to tell SQL Server that this is my function that will generate the identity values. But what I know is that no such function exists...
I want to...
Implement global data base IDs, I need to provide a default value that I'm in control of.
My idea was based around that I should be able to have a table with all known IDs and then every row ID from some other table that needed a global ID would reference that table. The default value would be provided by something like
INSERT INTO GlobalID
RETURN SCOPE_IDENTITY()
No; it's not unique if it can be reused.
Why do you want to re-use them? Why do you concern yourself with this field? If you want to be in control of it, don't make it an identity; create your own scheme and use that.
Don't reuse identities, you'll just shoot your self in the foot. Use a large enough value so that it never rolls over (64 bit big int).
To find missing gaps in a sequence of numbers join the table against itself with a +/- 1 difference:
SELECT a.id
FROM table AS a
LEFT OUTER JOIN table AS b ON a.id = b.id+1
WHERE b.id IS NULL;
This query will find the numbers in the id sequence for which id-1 is not in the table, ie. contiguous sequence start numbers. You can then use SET IDENTITY INSERT OFF to insert a specific id and reuse a number. The cost of doing so is overwhelming (both runtime and code complexity) compared with the an ordinary identity based insert.
If you really want to reset Identity value to the lowest,
here is the trick you can use through DBCC CHECKIDENT
Basically following sql statements resets identity value so that identity value restarts from the lowest possible number
create table TT (id int identity(1, 1))
GO
insert TT default values
GO 10
select * from TT
GO
delete TT where id between 5 and 10
GO
--; At this point, next ID will be 11, not 5
select * from TT
GO
insert TT default values
GO
--; as you can see here, next ID is indeed 11
select * from TT
GO
--; Now delete ID = 11
--; so that we can reseed next highest ID to 5
delete TT where id = 11
GO
--; Now, let''s reseed identity value to the lowest possible identity number
declare #seedID int
select #seedID = max(id) from TT
print #seedID --; 4
--; We reseed identity column with "DBCC CheckIdent" and pass a new seed value
--; But we can't pass a seed number as argument, so let's use dynamic sql.
declare #sql nvarchar(200)
set #sql = 'dbcc checkident(TT, reseed, ' + cast(#seedID as varchar) + ')'
exec sp_sqlexec #sql
GO
--; Now the next
insert TT default values
GO
--; as you can see here, next ID is indeed 5
select * from TT
GO
I guess we would really need to know why you want to reuse your identity column. The only reason I can think of is because of the temporary nature of your data you might exhaust the possible values for the identity. That is not really likely, but if that is your concern, you can use uniqueidentifiers (guids) as the primary key in your table instead.
The function newid() will create a new guid and can be used in insert statements (or other statements). Then when you delete the row, you don't have any "holes" in your key because guids are not created in that order anyway.
[Syntax assumes SQL2008....]
Yes, it's possible. You need to two management tables, and two triggers on each participating table.
First, the management tables:
-- this table should only ever have one row
CREATE TABLE NextId (Id INT)
INSERT NextId VALUES (1)
GO
CREATE TABLE RecoveredIds (Id INT NOT NULL PRIMARY KEY)
GO
Then, the triggers, two on each table:
CREATE TRIGGER tr_TableName_RecoverId ON TableName
FOR DELETE AS BEGIN
IF ##ROWCOUNT = 0 RETURN
INSERT RecoveredIds (Id) SELECT Id FROM deleted
END
GO
CREATE TRIGGER tr_TableName_AssignId ON TableName
INSTEAD OF INSERT AS BEGIN
DECLARE #rowcount INT = ##ROWCOUNT
IF #rowcount = 0 RETURN
DECLARE #required INT = #rowcount
DECLARE #new_ids TABLE (Id INT PRIMARY KEY)
DELETE TOP (#required) OUTPUT DELETED.Id INTO #new_ids (Id) FROM RecoveredIds
SET #rowcount = ##ROWCOUNT
IF #rowcount < #required BEGIN
DECLARE #output TABLE (Id INT)
UPDATE NextId SET Id = Id + (#required-#rowcount)
OUTPUT DELETED.Id INTO #output
-- this assumes you have a numbers table around somewhere
INSERT #new_ids (Id)
SELECT n.Number+o.Id-1 FROM Numbers n, #output o
WHERE n.Number BETWEEN 1 AND #required-#rowcount
END
SET IDENTITY_INSERT TableName ON
;WITH inserted_CTE AS (SELECT _no = ROW_NUMBER() OVER (ORDER BY Id), * FROM inserted)
, new_ids_CTE AS (SELECT _no = ROW_NUMBER() OVER (ORDER BY Id), * FROM #new_ids)
INSERT TableName (Id, Attr1, Attr2)
SELECT n.Id, i.Attr1, i.Attr2
FROM inserted_CTE i JOIN new_ids_CTE n ON i._no = n._no
SET IDENTITY_INSERT TableName OFF
END
You could script the triggers out easily enough from system tables.
You would want to test this for concurrency. It should work as is, syntax errors notwithstanding: The OUTPUT clause guarantees atomicity of id lookup->increment as one step, and the entire operation occurs within a transaction, thanks to the trigger.
TableName.Id is still an identity column. All the common idioms like $IDENTITY and SCOPE_IDENTITY() will still work.
There is no central table of ids by table, but you could create one easily enough.
I don't have any help for finding the values not in use but if you really want to find them and set them yourself, you can use
set identity_insert on ....
in your code to do so.
I'm with everyone else though. Why bother? Don't you have a business problem to solve?

Weird trigger problem when I do an INSERT into a table

I've got a trigger attached to a table.
ALTER TRIGGER [dbo].[UpdateUniqueSubjectAfterInsertUpdate]
ON [dbo].[Contents]
AFTER INSERT,UPDATE
AS
BEGIN
-- Grab the Id of the row just inserted/updated
DECLARE #Id INT
SELECT #Id = Id
FROM INSERTED
END
Every time a new entry is inserted or modified, I wish to update a single field (in this table). For the sake of this question, imagine i'm updating a LastModifiedOn (datetime) field.
Ok, so what i've got is a batch insert thingy..
INSERT INTO [dbo].[Contents]
SELECT Id, a, b, c, d, YouDontKnowMe
FROM [dbo].[CrapTable]
Now all the rows are correctly inserted. The LastModifiedOn field defaults to null. So all the entries for this are null -- EXCEPT the first row.
Does this mean that the trigger is NOT called for each row that is inserted into the table, but once AFTER the insert query is finished, ie. ALL the rows are inserted? Which mean, the INSERTED table (in the trigger) has not one, but 'n' number of rows?!
If so .. er.. :( Would that mean i would need a cursor in this trigger? (if i need to do some unique logic to each single row, which i do currently).
?
UPDATE
I'll add the full trigger code, to see if it's possible to do it without a cursor.
BEGIN
SET NOCOUNT ON
DECLARE #ContentId INTEGER,
#ContentTypeId TINYINT,
#UniqueSubject NVARCHAR(200),
#NumberFound INTEGER
-- Grab the Id. Also, convert the subject to a (first pass, untested)
-- unique subject.
-- NOTE: ToUriCleanText just replaces bad uri chars with a ''.
-- eg. an '#' -> ''
SELECT #ContentId = ContentId, #ContentTypeId = ContentTypeId,
#UniqueSubject = [dbo].[ToUriCleanText]([Subject])
FROM INSERTED
-- Find out how many items we have, for these two keys.
SELECT #NumberFound = COUNT(ContentId)
FROM [dbo].[Contents]
WHERE ContentId = #ContentId
AND UniqueSubject = #UniqueSubject
-- If we have at least one identical subject, then we need to make it
-- unique by appending the current found number.
-- Eg. The first instance has no number.
-- Second instance has subject + '1',
-- Third instance has subject + '2', etc...
IF #NumberFound > 0
SET #UniqueSubject = #UniqueSubject + CAST(#NumberFound AS NVARCHAR(10))
-- Now save this change.
UPDATE [dbo].[Contents]
SET UniqueSubject = #UniqueSubject
WHERE ContentId = #ContentId
END
Why not change the trigger to deal with multiple rows?
No cursor or loops needed: it's the whole point of SQL ...
UPDATE
dbo.SomeTable
SET
LastModifiedOn = GETDATE()
WHERE
EXIST (SELECT * FROM INSERTED I WHERE I.[ID] = dbo.SomeTable.[ID]
Edit: Something like...
INSERT #ATableVariable
(ContentId, ContentTypeId, UniqueSubject)
SELECT
ContentId, ContentTypeId, [dbo].[ToUriCleanText]([Subject])
FROM
INSERTED
UPDATE
[dbo].[Contents]
SET
UniqueSubject + CAST(NumberFound AS NVARCHAR(10))
FROM
--Your original COUNT feels wrong and/or trivial
--Do you expect 0, 1 or many rows.
--Edit2: I assume 0 or 1 because of original WHERE so COUNT(*) will suffice
-- .. although, this implies an EXISTS could be used but let's keep it closer to OP post
(
SELECT ContentId, UniqueSubject, COUNT(*) AS NumberFound
FROM #ATableVariable
GROUP BY ContentId, UniqueSubject
HAVING COUNT(*) > 0
) foo
JOIN
[dbo].[Contents] C ON C.ContentId = foo.ContentId AND C.UniqueSubject = foo.UniqueSubject
Edit 2: and again with RANKING
UPDATE
C
SET
UniqueSubject + CAST(foo.Ranking - 1 AS NVARCHAR(10))
FROM
(
SELECT
ContentId, --not needed? UniqueSubject,
ROW_NUMBER() OVER (PARTITION BY ContentId ORDER BY UniqueSubject) AS Ranking
FROM
#ATableVariable
) foo
JOIN
dbo.Contents C ON C.ContentId = foo.ContentId
/* not needed? AND C.UniqueSubject = foo.UniqueSubject */
WHERE
foo.Ranking > 1
The trigger will be run only once for an INSERT INTO query. The INSERTED table will contain multiple rows.
Ok folks, I think I figure it out myself. Inspired by the previous answers and comments, I've done the following. (Can you folks have a quick look over to see if i've over-enginered this baby?)
.1. Created an Index'd View, representing the 'Subject' field, which needs to be cleaned. This is the field that has to be unique .. but before we can make it unique, we need to group by it.
-- Create the view.
CREATE VIEW ContentsCleanSubjectView with SCHEMABINDING AS
SELECT ContentId, ContentTypeId,
[dbo].[ToUriCleanText]([Subject]) AS CleanedSubject
FROM [dbo].[Contents]
GO
-- Index the view with three index's. Custered PK and a non-clustered,
-- which is where most of the joins will be done against.
-- Last one is because the execution plan reakons i was missing statistics
-- against one of the fields, so i added that index and the stats got gen'd.
CREATE UNIQUE CLUSTERED INDEX PK_ContentsCleanSubjectView ON
ContentsCleanSubjectView(ContentId)
CREATE NONCLUSTERED INDEX IX_BlahBlahSnipSnip_A ON
ContentsCleanSubjectView(ContentTypeId, CleanedSubject)
CREATE INDEX IX_BlahBlahSnipSnip_B ON
ContentsCleanSubjectView(CleanedSubject)
.2. Create the trigger code which now
a) grabs all the items 'changed' (nothing new/hard about that)
b) orders all the inserted rows, row numbered with partitioning by a clean subject
c) update the single row we're upto in the main update clause.
here's the code...
ALTER TRIGGER [dbo].[UpdateUniqueSubjectAfterInsertUpdate]
ON [dbo].[Contents]
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON
DECLARE #InsertRows TABLE (ContentId INTEGER PRIMARY KEY,
ContentTypeId TINYINT,
CleanedSubject NVARCHAR(300))
DECLARE #UniqueSubjectRows TABLE (ContentId INTEGER PRIMARY KEY,
UniqueSubject NVARCHAR(350))
DECLARE #UniqueSubjectRows TABLE (ContentId INTEGER PRIMARY KEY,
UniqueSubject NVARCHAR(350))
-- Grab all the records that have been updated/inserted.
INSERT INTO #InsertRows(ContentId, ContentTypeId, CleanedSubject)
SELECT ContentId, ContentTypeId, [dbo].[ToUriCleanText]([Subject])
FROM INSERTED
-- Determine the correct unique subject by using ROW_NUMBER partitioning.
INSERT INTO #UniqueSubjectRows
SELECT SubResult.ContentId, UniqueSubject = CASE SubResult.RowNumber
WHEN 1 THEN SubResult.CleanedSubject
ELSE SubResult.CleanedSubject + CAST(SubResult.RowNumber - 1 AS NVARCHAR(5)) END
FROM (
-- Order all the cleaned subjects, partitioned by the cleaned subject.
SELECT a.ContentId, a.CleanedSubject, ROW_NUMBER() OVER (PARTITION BY a.CleanedSubject ORDER BY a.ContentId) AS RowNumber
FROM ContentsCleanSubjectView a
INNER JOIN #InsertRows b ON a.ContentTypeId = b.ContentTypeId AND a.CleanedSubject = b.CleanedSubject
GROUP BY a.contentId, a.cleanedSubject
) SubResult
INNER JOIN [dbo].[Contents] c ON c.ContentId = SubResult.ContentId
INNER JOIN #InsertRows d ON c.ContentId = d.ContentId
-- Now update all the effected rows.
UPDATE a
SET a.UniqueSubject = b.UniqueSubject
FROM [dbo].[Contents] a INNER JOIN #UniqueSubjectRows b ON a.ContentId = b.ContentId
END
Now, the subquery correctly returns all the cleaned subjects, partitioned correctly and numbered correctly. I never new about the 'PARTITION' command, so that trick was the big answer here :)
Then i just join'd the subquery with the row that is being updated in the parent query. The row number is correct, so now i just do a case. if this is the first time the cleaned subject exists (eg. row_number = 1), don't modify it. otherwise, append the row_number minus one. This means the 2nd instance of the same subject, the unique subject will be => cleansubject + '1'.
The reason why i believe i need to have an index'd view is because if i have two very similar subjects, that when you have stripped out (ie. cleaned) all the bad chars (which i've determined are bad) .. it's possible that the two clean subjects are the same. As such, I need to do all my joins on a cleanedSubject, instead of a subject. Now, for the massive amount of rows I have, this is crap for performance when i don't have the view. :)
So .. is this over engineered?
Edit 1:
Refactored trigger code so it's waay more performant.

scope_identity vs ident_current

After much research I am a little confused by which identity tracker I should use in sql.
From what I understand scope_identity will give me the last id updated from any table and ident_current will will return the last id from a specified table.
So given that information it would seem to me the best version to use (if you know which table you will be updating) is ident_current. Yet, upon reading it seems most people prefer to use scope_identity. What is the reasoning behind this and is there a flaw in my logic?
In that case you need to write the table name, what happens if you decide to change the table name? You then also must not forget to update your code to reflect that. I always use SCOPE_IDENTITY unless I need the ID from the insert that happens in a trigger then I will use ##IDENTITY
Also the bigger difference is that IDENT_CURRENT will give you the identity from another process that did the insert (in other words last generated identity value from any user)
so if you do an insert and then someone does an insert before you do a SELECT IDENT_CURRENT you will get that other person's identity value
See also 6 Different Ways To Get The Current Identity Value which has some code explaining what happens when you put triggers on the table
From what I've read scope_identity() should be the right answer, however it looks like there is a bug in SQL 2005 and SQL 2008 that can come into play if your insert results in a parallel query plan.
Take a look at the following articles for more details:
##IDENTITY vs SCOPE_IDENTITY() vs IDENT_CURRENT - Retrieve Last Inserted Identity of Record
Article: Six reasons you should be nervous about parallelism
See section titled: 1. #328811, "SCOPE_IDENTITY() sometimes returns incorrect value"
See this blogpost for your answer in detail. Scope_identity will never return identities due to inserts done by triggers. It wont be a great idea to use ident_current in a world of change where tablenames are changed..like in a dev env.
/*
* IDENT_CURRENT returns the last identity value generated for a specific table in any session and any scope.
* ##IDENTITY returns the last identity value generated for any table in the current session, across all scopes.
* SCOPE_IDENTITY returns the last identity value generated for any table in the current session and the current scope.
*/
IF OBJECT_ID(N't6', N'U') IS NOT NULL
DROP TABLE t6 ;
GO
IF OBJECT_ID(N't7', N'U') IS NOT NULL
DROP TABLE t7 ;
GO
CREATE TABLE t6 (id INT IDENTITY) ;
CREATE TABLE t7
(
id INT IDENTITY(100, 1)
) ;
GO
CREATE TRIGGER t6ins ON t6
FOR INSERT
AS
BEGIN
INSERT t7
DEFAULT VALUES
END ;
GO
--End of trigger definition
SELECT id
FROM t6 ;
--IDs empty.
SELECT id
FROM t7 ;
--ID is empty.
--Do the following in Session 1
INSERT t6
DEFAULT VALUES ;
SELECT ##IDENTITY ;
/*Returns the value 100. This was inserted by the trigger.*/
SELECT SCOPE_IDENTITY() ;
/* Returns the value 1. This was inserted by the
INSERT statement two statements before this query.*/
SELECT IDENT_CURRENT('t7') ;
/* Returns 100, the value inserted into t7, that is in the trigger.*/
SELECT IDENT_CURRENT('t6') ;
/* Returns 1, the value inserted into t6 four statements before this query.*/
-- Do the following in Session 2.
SELECT ##IDENTITY ;
/* Returns NULL because there has been no INSERT action
up to this point in this session.*/
SELECT SCOPE_IDENTITY() ;
/* Returns NULL because there has been no INSERT action
up to this point in this scope in this session.*/
SELECT IDENT_CURRENT('t7') ;
/* Returns 100, the last value inserted into t7.*/
The theory says: To be aware of race conditions and to do not care about inserts inside triggers, you should be using SCOPE_IDENTITY() BUT ... there are know bugs on SCOPE_IDENTITY() (and ##IDENTITY) as is mentioned and linked on other anwsers. Here are the workarounds from Microsoft that takes into account this bugs.
Below the most relevant part from the article. It uses output insert's clause:
DECLARE #MyNewIdentityValues table(myidvalues int)
declare #A table (ID int primary key)
insert into #A values (1)
declare #B table (ID int primary key identity(1,1), B int not null)
insert into #B values (1)
select
[RowCount] = ##RowCount,
[##IDENTITY] = ##IDENTITY,
[SCOPE_IDENTITY] = SCOPE_IDENTITY()
set statistics profile on
insert into _ddr_T
output inserted.ID into #MyNewIdentityValues
select
b.ID
from #A a
left join #B b on b.ID = 1
left join #B b2 on b2.B = -1
left join _ddr_T t on t.T = -1
where not exists (select * from _ddr_T t2 where t2.ID = -1)
set statistics profile off
select
[RowCount] = ##RowCount,
[##IDENTITY] = ##IDENTITY,
[SCOPE_IDENTITY] = SCOPE_IDENTITY(),
[IDENT_CURRENT] = IDENT_CURRENT('_ddr_T')
select * from #MyNewIdentityValues
go
SELECT IDENT_CURRENT -- as you said will give you the table specific last inserted identity value. There are issues associated with this, one the user need to have permission to see the metadata otherwise it returns NULL and second you are hardcoding the name of the table , which will cause a problem in case the table name changes.
The best practice is to use Scope_Identity together with a variable ...Look the following example
DECLARE #myFirstTableID INT
DECLARE #mySecondTableID INT
INSERT INTO MYFirstTable (....) VALUES (.....)
SELECT #myFirstTableID =SCOPE_IDENTITY()
INSERT INTO MYSecondTable () VALUES (.....)
SELECT #mySecondTableID=SCOPE_IDENTITY()
Thus by making use of variable and scope_identity next to the insert statement of interest, you can make sure that you are getting the right identity from the right table.
Enjoy