SQL Server Identity Issues - sql

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.

Related

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)

How to keep a variable in sql stored procedure constant and unchanged in every call to the stored procedure

I have a stored procedure up_InsertEmployees. I have a functionality where I am uploading a batch of employee details into the web application. This functionality inserts the employees details into the DB using the above mentioned stored procedure.
The stored procedure goes something like this
create procedure 'sp_InsertEmployees'
(#EmployeeUN,
#FirstName,
#LastName,
#City, #Ref)
BEGIN
declare #BatchRef varchar(20)
set #BatchRef = #Ref+GetUTCDate()
Insert into Employee(EmployeeUN, FirstName, LastName, City, BatchRef)
Values(#EmployeeUN, #FirstName, #LastName, #City, #BatchRef)
END
Here the column Ref holds the reference of the batch upload that I have performed. The value BatchRef has to be the same for all the employees of that particular batch. Since I am using GetUTCDate() the value of BatchRef might change with every employee that is being inserted. Can somebody please let me know how I can calculate the value of BatchRef when the first employee is being inserted and then keep it constant there on? I want this to be done in SQL only and not in the code. And also I want the datetime value to be in BatchRef so that each batch values are unique.
The best way to keep a consistent BatchRef value across multiple rows being inserted is to insert all of the rows from that batch at the same time ;-). And doing so will also have the benefit of being quite a bit more efficient :-).
Assuming you are using SQL Server 2008 or newer, this can be accomplish via Table-Valued Parameters (TVPs). I have detailed the approach in my answer to the following question:
Pass Dictionary<string,int> to Stored Procedure T-SQL
For this particular use-case, the stored procedure would look something like the following:
CREATE PROCEDURE InsertEmployees
(
#EmployeeBatch dbo.EmployeeData READONLY,
#Ref VARCHAR(50)
)
BEGIN
DECLARE #BatchRef VARCHAR(50);
SET #BatchRef = #Ref + CONVERT(VARCHAR(30), GetUTCDate(), 121);
INSERT INTO Employee (EmployeeUN, FirstName, LastName, City, BatchRef)
SELECT eb.EmployeeUN, eb.FirstName, eb.LastName, eb.City, #BatchRef
FROM #EmployeeBatch eb;
END
Anyone stuck on SQL Server 2005 can accomplish the same basic concept using XML. It won't be as efficient as the entire XML document needs to be created in the app code and then parsed into a temp table in the stored procedure, but it is still a lot faster than doing row-by-row operations.
Given:
#Ref is unique per each batch and never reused
#Ref needs to stay unique per each batch, not unique per each Employee that is uploaded
There is no option to alter the upload process from one call per Employee to doing the entire batch as a single set
You want the time that the batch started (i.e. when the first Employee in each batch is uploaded)
Then:
Don't combine #Ref with GETUTCDATE(), but instead track the start time in another table.
CREATE TABLE dbo.EmployeeBatchStartTime
(
BatchRef VARCHAR(50) NOT NULL
CONSTRAINT [PK_EmployeeBatchStartTime] PRIMARY KEY,
StartTime DATETIME NOT NULL
CONSTRAINT [DF_EmployeeBatchStartTime_StartTime] DEFAULT (GETUTCDATE())
);
Then you can check to see if a row for the passed-in value of #Ref exists in that table and if not, insert it, else skip it.
CREATE PROCEDURE InsertEmployee
(
#EmployeeUN DataType,
#FirstName DataType,
#LastName DataType,
#City DataType,
#Ref VARCHAR(50)
)
AS
SET NOCOUNT ON;
IF (NOT EXISTS(
SELECT *
FROM dbo.EmployeeBatchStartTime
WHERE BatchRef = #Ref
)
)
BEGIN
INSERT INTO dbo.EmployeeBatchStartTime (BatchRef)
VALUES (#Ref);
END;
INSERT INTO Employee (EmployeeUN, FirstName, LastName, City, BatchRef)
VALUES (#EmployeeUN, #FirstName, #LastName, #City, #BatchRef);
This not only lets you keep the value of BatchRef clean and usable (instead of combining a DATETIME value into it) but also gives you a DATETIME value that is usable without any error-prone text parsing or conversion from a string into a DATETIME datatype. This means that you can even add an index, if need be, to the StartTime field in the EmployeeBatchStartTime which will allow you to JOIN to that table on BatchRef and then use StartTime in an ORDER BY and it will be rather efficient :). AND, this requires no change at all to the existing app code :) :).

procedure to take information from one table and write to another table

Ive got a question which I am completely stumped on. It reads as follows:
Write a import stored procedure that take information from a table (source) and write it into another table (target).
Im guessing it would look something like the following:
create procedure add_data(#name, #surname)
AS
BEGIN
SELECT name, surname
FROM cardholder
INSERT INTO new_table
values (name, surname)
Is my logic correct or am I completely missing it?
You are almost there. Since you are directly copying from one table to another, you can use the INSERT INTO ... SELECT ... idiom, like so:
create procedure add_data
#name varchar(100)
#surname varchar(100)
AS
BEGIN
INSERT INTO new_table(name,surname)
SELECT name, surname
FROM cardholder
END
Note the 2 changes I made:
How you declare the parameters for the SP
The values clause should consist of the actual values being inserted. Since you are inserting data retrieved from a table rather than fixed values, you use the select query instead.
Try the following code.
create procedure add_data(#name VARCHAR(10), #surname VARCHAR(10))
AS
BEGIN
INSERT INTO new_table(name, surname)
SELECT name, surname
FROM cardholder
END
It would insert data from one table to another so in output what ever the parameters pass it will inserted into new table just plain and simple.
It is not correct. try dis
create procedure add_data
(
#name varchar(100),
#surname varchar(100)
)
AS
BEGIN
INSERT INTO target
SELECT name, surname
FROM cardholder
end

How to insert an IDENT_CURRENT on INSERT query?

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().