How to insert an IDENT_CURRENT on INSERT query? - sql

I'm creating a Stored Procedure in MS SQL SERVER 2008 to enter the clients. Here is the important part of my code:
INSERT INTO Entity VALUES (#nameEnt, #iniEnt, #LastNEnt1, #suffixEnt);
INSERT INTO Patient VALUES (#housing, #dob, IDENT_CURRENT('Entity'));
INSERT INTO Direction VALUES (#postAdd, #city, #state, #zip, IDENT_CURRENT('Entity'));
INSERT INTO PatientSecure VALUES (IDENT_CURRENT('Patient'), #idSecure, #contractNo, #groupNo, #coverage)
It doesn't work, probably because I only have seen IDENT_CURRENT used on SELECT. However, I need to do something similar, which the idEntity generated on Entity after the first line in inserted, be on the Patient table on the row idEntity which has to be the same id generated on Entity. The same thing on Direction and PatientSecure.
If there is a better way to do this, please suggest it.
Please help! and be nice :)
Thank You

Note that IDENT_CURRENT returns a value that is independent of session and scope. A different user might cause the value the change between your INSERT statements.
You can use Scope_Identity():
declare #EntityId as Int;
INSERT INTO Entity VALUES (#nameEnt, #iniEnt, #LastNEnt1, #suffixEnt);
set #EntityId = Scope_Identity();
declare #PatientId as Int;
INSERT INTO Patient VALUES (#housing, #dob, #EntityId);
set #PatientId = Scope_Identity();
INSERT INTO Direction VALUES (#postAdd, #city, #state, #zip, #EntityId);
INSERT INTO PatientSecure VALUES (#PatientId, #idSecure, #contractNo, #groupNo, #coverage)
Or you could use an OUTPUT clause on the INSERTs to save the appropriate values. While it may be overkill in this case, it is a valuable tool to know. It works with DELETE, INSERT and UPDATE statements, handles multiple rows and (where applicable) provides access to before and after values.

I would replace all of your calls to ident_current('some_table') with calls to scope_identity().

Related

Confused by SCOPE_IDENTITY() and GO

I am a bit confused by the documentation & behavior of SCOPE_IDENTITY() in SQL Server.
This page https://learn.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql?view=sql-server-2017 says this about SCOPE_IDENTITY():
Returns the last identity value inserted into an identity column in
the same scope. A scope is a module: a stored procedure, trigger,
function, or batch. Therefore, if two statements are in the same
stored procedure, function, or batch, they are in the same scope.
And it contains this example
USE AdventureWorks2012;
GO
INSERT INTO Person.ContactType ([Name]) VALUES ('Assistant to the Manager');
GO
SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];
GO
SELECT ##IDENTITY AS [##IDENTITY];
GO
Which returns
SCOPE_IDENTITY
21
##IDENTITY
21
From the docs I would have thought that the result of SCOPE_IDENTITY() would be NULL because SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY]; is executed in a different batch (because it comes after GO) than the INSERT command... What am I missing here?
I'd agree, I think the documentation is slightly misleading. SCOPE_IDENTITY does retain its value across multiple batches directly executed on the same connection.
But note that if you create an inner batch, by executing EXEC with a string, that inner batch's SCOPE_IDENTITY is independent from your outer batch's SCOPE_IDENTITY
This script produces the value 2, not 5:
create table T1 (ID int IDENTITY(2,1000) not null,Val char(1))
create table T2 (ID int IDENTITY(5,1000) not null, Val char(1))
go
insert into T1(Val) values ('a')
exec('insert into T2(Val) values (''b'')')
select SCOPE_IDENTITY()
Don't use scope_indentity. SQL Server has a much, much better way of returning values from the insert, the OUTPUT clause.
DECLARE #ids TABLE (id INT);
INSERT INTO Person.ContactType ([Name])
OUTPUT inserted.ID INTO #ids -- I'm not sure what the identity is named
VALUES ('Assistant to the Manager');
This has multiple advantages:
You can return more columns than just the id.
You can return the ids from inserts that have more than one row.
You don't have to worry about scoping at all.

How to create a temporary variable equal to the id of the last inserted row

SQL n00b here. Any idea what I'm doing wrong? Hopefully you can figure out what I'm intending to to.
DECLARE #CatId INT;
SET #CatId = (
INSERT INTO Categories (CategoryName) VALUES ('TestCategory');
SELECT SCOPE_IDENTITY()
);
INSERT INTO Fields (CategoryID,FieldName,DisplayName) VALUES (#CatId,'TestName','TestDisplayName');
I'm getting the not-very-detailed error
Incorrect syntax near 'INSERT'
In SQL Server, the right way to handle this is the OUTPUT clause. I would recommend that you learn this method and stick to it.
DECLARE #CatIds TABLE (catid int);
INSERT INTO Categories (CategoryName)
OUTPUT inserted.catid INTO #CatIds;
VALUES ('TestCategory');
INSERT INTO Fields(CategoryID, FieldName, DisplayName)
SELECT CatId, 'TestName', 'TestDisplayName'
FROM #CatIds;
This is preferable to other methods because it explicitly captures the desired information into a table, which can then be further processed. Code is more robust because there is no danger of adding a line of code "in between" and breaking the existing code. It also supports inserts of multiple values at the same time.
You don't even need a variable for this. Remember the KISS principal.
INSERT INTO Categories (CategoryName) VALUES ('TestCategory');
INSERT INTO Fields (CategoryID,FieldName,DisplayName) VALUES (SCOPE_IDENTITY(),'TestName','TestDisplayName');
You can use IDENT_CURRENT
DECLARE #CatId INT
SELECT #CatId = IDENT_CURRENT('Categories')
Then reuse the #CatId in the INSERT
INSERT INTO Fields (CategoryID, FieldName, DisplayName) VALUES (#CatId,'TestName','TestDisplayName');
IDENT_CURRENT returns the last identity value generated for a specific table in any session and any scope.
If you need it to be only scoped to your session you can use SCOPE_IDENTITY

How to insert values into two tables with a foreign key relationship?

I created two tables:
Table tblStaff with columns id (primary key, auto increment), name, age, address
Table tblRoleOfStaff with columns id (primary key, auto increment), StaffId (foreign key to tblStaff), RoleId
I have form to create new staff with existing role. Data sample to insert:
(name, age, address, roleId) = ('my name',20,'San Jose', 1)
I want to write a stored procedure in SQL Server 2014 to insert new staff to tblStaff and insert new record into tbleRoleOfStaff with staffId I just inserted.
What should I do?
I am so sorry if my question is duplicate with other. I am fresher in SQL.
Thanks for any help.
Use SCOPE_IDENTITY() second insert into tblRoleOfStuff on a place of StaffId. Like:
insert into tblStaff values
(#name, #age, #address)
insert into tblRoleOfStuff values
(scope_identity(), #roleid)
EDIT
There too much comments on this answer, so I want to give an explanation.
If OP guarantee that he will not use any triggers he may use ##IDENTITY (bad practice), it is sufficient enough to his needs, but best practice to use SCOPE_IDENTITY().
SCOPE_IDENTITY(), like ##IDENTITY, will return the last identity value created in the current session, but it will also limit it to your current scope as well. In other words, it will return the last identity value that you explicitly created, rather than any identity that was created by a trigger or a user defined function.
SCOPE_IDENTITY() will guarantee that you get identity from current operation, not from another connection or last one processed.
Why not IDENT_CURRENT? Because
IDENT_CURRENT is not limited by scope and session; it is limited to a specified table. IDENT_CURRENT returns the identity value generated for a specific table in any session and any scope.
So you make take last scoped but not current one. Yes, OP can use it too, but it is a bad practice in that situation (like using only ##IDENTITY)
Using OUTPUT is indeed good practice, but over complicated for only one identity. If OP need to process more then one row in a time - yes, he need OUTPUT.
Because it seems like you are discussing 1 row at a time some people may tell you to use a system vairable like ##IDENTITY or some of the others but to ensure with more certainty I recommend the OUTPUT clause of the insert statement. The good thing about this method is it can easily be adapted to handle more than 1 row at a time.
DECLARE #Output AS TABLE (StaffId INT)
INSERT INTO tblStaff (name, age, address)
OUTPUT inserted.Id INTO #Output (StaffId)
VALUES (#name, #age, #address)
DECLARE #StaffId INT
SELECT #StaffId = StaffId FROM #Output
INSERT INTO tblRoleOfStaff (StaffId, RoleId)
VALUES (#StaffId,#RoleId)
Reasons not to use ##IDENTITY in case another operation linked to yours is performed. E.g. a trigger inserts another row into another table, or updates another record in your database., SCOPE_IDENTITY has a similar shortfall when a trigger modifies the same table. IDENT_CURRENT has a short coming too. Do an internet search to learn more there are tons of great resources on these.
You can use output from your first insert statement.
declare #tmp table(id int)
insert tblStaff (name, age, address)
OUTPUT inserted.Id INTO #tmp (id)
values (#name, #age, #address)
declare #roleId int = 1 --or whatever
insert tblRoleOfStaff (staffId,roleId)
select id, #roleId
from #tmp
You can insert several roles at once as well.
create table Roles (roleId int identity(1,1) primary key,
RoleName varchar(50),
isDefaultRole bit default 0
)
--mark some roles as default (`isDefaultRole = 1`)
--the 2nd insert will be
insert tblRoleOfStaff (staffId,roleId)
select id, roleId
from #tmp
cross join Roles
where isDefaultRole = 1
If it is just one staff row that you would insert at a time, you could do the following:
begin try
begin tran
insert into tblStaff (name, age, address) values('my name',20,'San Jose');
insert into tbleRoleOfStaff (StaffId, RoleId) values (SCOPE_IDENTITY(), 1);
commit
end try
begin catch
IF ##trancount > 0 ROLLBACK;
end catch
Try this:
Create Procedure Pro_XXX()
AS
BEGIN
INSERT INTO tblStaff (name, age, address, roleId) VALUES ('my name',20,'San Jose', 1);
INSERT INTO tbleRoleOfStaff VALUES (staffId, roleId) VALUES (IDENT_CURRENT('tblStaff'),0)
END
Please note to the differences between IDENT_CURRENT, SCOPE_IDENTITY and ##IDENTITY. Read about it Here
After the insert into the first table use something like
DECLARE #staffId INT
SET #staffId = (SELECT TOP 1 id from tblStaff order by id desc)
INSERT INTO tblRoleOfStaff (staffId,roleId) VALUES (#staffId, 2)

SQL Server Identity Issues

Looking for some help, as this SQL thing is new to me. I was required to copy data from one table to another used for a billing application. To do so, I simply copied the entire table using SELECT INTO statement. The application now refuses to insert new records into the table that I had copied. I am using SQL Server 2008, and suggest it has something to do with the Identity column, but not sure. As stated above, this is all new to me. Any help or suggestions where to start would be awesome. Thank-you in advance.
The table that I copies looked like such...
InsuranceID int IDENTITY,
Insurance_ID varchar(20),
Insurance_Name varchar(100),
Address varchar(100),
.. And so on.
This is the stored procedure used to insert new records into the table.
Insert Into tblInsuranceCompanies
(Insurance_ID,
InsuranceName,
InsuranceTypeID,
Telephone,
Extension,
Fax,
Address,
City,
State,
Zip_Code)
Values
(#InsuranceID,
#Name,
#TypeID,
#Telephone,
#Extension,
#Fax,
#Address,
#City,
#State,
#Zip)
SELECT ##IDENTITY AS InsuranceID
First I thought it is an Identity_Insert problem, which I suggested this:
before trying to insert data you can use SET IDENTITY_INSERT products ON
like this:
SET IDENTITY_INSERT products ON
GO
INSERT INTO YOUR_TABLE
SELETC * FROM TempTable
GO
SET IDENTITY_INSERT products OFF
GO
but after noticing that the Identity column is not there in the select list, and since all the columns definitions are NOT NULL, i guess one of the values you are trying to insert is null which is causing the error.

SQL Server - Get Inserted Record Identity Value when Using a View's Instead Of Trigger

For several tables that have identity fields, we are implementing a Row Level Security scheme using Views and Instead Of triggers on those views. Here is a simplified example structure:
-- Table
CREATE TABLE tblItem (
ItemId int identity(1,1) primary key,
Name varchar(20)
)
go
-- View
CREATE VIEW vwItem
AS
SELECT *
FROM tblItem
-- RLS Filtering Condition
go
-- Instead Of Insert Trigger
CREATE TRIGGER IO_vwItem_Insert ON vwItem
INSTEAD OF INSERT
AS BEGIN
-- RLS Security Checks on inserted Table
-- Insert Records Into Table
INSERT INTO tblItem (Name)
SELECT Name
FROM inserted;
END
go
If I want to insert a record and get its identity, before implementing the RLS Instead Of trigger, I used:
DECLARE #ItemId int;
INSERT INTO tblItem (Name)
VALUES ('MyName');
SELECT #ItemId = SCOPE_IDENTITY();
With the trigger, SCOPE_IDENTITY() no longer works - it returns NULL. I've seen suggestions for using the OUTPUT clause to get the identity back, but I can't seem to get it to work the way I need it to. If I put the OUTPUT clause on the view insert, nothing is ever entered into it.
-- Nothing is added to #ItemIds
DECLARE #ItemIds TABLE (ItemId int);
INSERT INTO vwItem (Name)
OUTPUT INSERTED.ItemId INTO #ItemIds
VALUES ('MyName');
If I put the OUTPUT clause in the trigger on the INSERT statement, the trigger returns the table (I can view it from SQL Management Studio). I can't seem to capture it in the calling code; either by using an OUTPUT clause on that call or using a SELECT * FROM ().
-- Modified Instead Of Insert Trigger w/ Output
CREATE TRIGGER IO_vwItem_Insert ON vwItem
INSTEAD OF INSERT
AS BEGIN
-- RLS Security Checks on inserted Table
-- Insert Records Into Table
INSERT INTO tblItem (Name)
OUTPUT INSERTED.ItemId
SELECT Name
FROM inserted;
END
go
-- Calling Code
INSERT INTO vwItem (Name)
VALUES ('MyName');
The only thing I can think of is to use the IDENT_CURRENT() function. Since that doesn't operate in the current scope, there's an issue of concurrent users inserting at the same time and messing it up. If the entire operation is wrapped in a transaction, would that prevent the concurrency issue?
BEGIN TRANSACTION
DECLARE #ItemId int;
INSERT INTO tblItem (Name)
VALUES ('MyName');
SELECT #ItemId = IDENT_CURRENT('tblItem');
COMMIT TRANSACTION
Does anyone have any suggestions on how to do this better?
I know people out there who will read this and say "Triggers are EVIL, don't use them!" While I appreciate your convictions, please don't offer that "suggestion".
You could try SET CONTEXT_INFO from the trigger to be read by CONTEXT_INFO() in the client.
We use it the other way to pass info into the trigger but would work in reverse.
Have you in this case tried ##identity? You mentioned both scope_Identity() and identity_current() but not ##identity.