Primary Key Error while inserting record into table - sql

I am passing datatable as input parameter to a stored procedure. I have created custom type for it.
Here is my stored procedure:
CREATE PROCEDURE [dbo].[Proc_AddEmployee]
#tblEmp EmpType READONLY,
#Code int
AS
BEGIN
INSERT INTO Employee ([Name], [Lname], [Code])
SELECT
[Name], [Lname], #Code
FROM #tblEmp
Here it fetch record from datatable and insert into Employee table.
Table Employee has primary key (combination Name and Lname).
Table Employee:
Nmae LName Code
Rashmi Hirve 89
Rani Mohite 7
DataTable :
Nmae LName
Rani Mohite
Swati More
Reshma Gawade
Problem appears when I try to add record (Rani, Mohite) from datatable to table Employee.
It causes a primary key error at first record and does not proceed further.
I want like this if error come skip that record fetch next record and insert that. There are 8000 records, which I want to pass from datatable to Employee table.
If I checked not exist, then insert will take long time to execute query.How to handle that?

Adding a check for EXISTS on the INSERT statement should not have a significant effect on performance.
INSERT INTO Employee ([Name] ,[Lname] ,[Code])
SELECT [Name] ,[Lname] ,#Code
FROM #tblEmp AS t
WHERE NOT EXISTS
( SELECT 1
FROM Employee AS e
WHERE e.Name = t.Name
AND e.Lname = t.Lname
);
This is fairly safe, but still vulnerable to a race condition. I think the safest way to do the insert, with is to use MERGE with the locking hint HOLDLOCK:
MERGE Employee WITH(HOLDLOCK) AS t
USING #tbl AS s
ON s.Name = t.Name
AND s.LName = t.LName
WHEN NOT MATCHED THEN
INSERT ([Name] ,[Lname] ,[Code])
VALUES (s.Name, s.Lname, #Code);

If the table has a primary key that is not set to auto generate then it will error when you try to insert a record without the key. You will need to either set the primary key field as an identity seed or you can include the primary key with the insert.

Related

SQL Server : primary key violation on occasion despite prior deletion

I have a procedure that is supposed to replace an entry in my table by first deleting it based on an id and then inserting it with new values.
It looks like this, plus additional values used for the insert statement that I have left out.
ALTER PROCEDURE [dbo].[DemoProc]
#id BIGINT,
...
AS
IF EXISTS (SELECT id FROM demoTable WHERE id = #id)
DELETE demoTable
WHERE id = #id
INSERT INTO demoTable (id, ...)
VALUES (#id, ...)
RETURN 0
Now every now and again, I'm getting an error log that alerts me of a primary key violation that originates from this procedure. I also end up with the entry not being inserted at all. Any ideas?

How to get ID of an identity column?

I have a many-to-many relationship between two tables, Users and Projects.
The table that connects those two together is named ProjectsUsers.
Here is the description of the tables and their relationships:
CREATE TABLE "Users"
(
Email VARCHAR(320) COLLATE SQL_Latin1_General_CP1_CI_AS PRIMARY KEY CHECK(LEN(Email) >= 3),
--More....
);
CREATE TABLE "Projects"
(
ProjectID INT PRIMARY KEY IDENTITY,
--More....
);
CREATE TABLE "ProjectsUsers"
(
UsersEmail VARCHAR(320) COLLATE SQL_Latin1_General_CP1_CI_AS CHECK(LEN(UsersEmail) >= 3) NOT NULL,
ProjectsID INT NOT NULL,
CONSTRAINT ProjectsUsers_PK PRIMARY KEY (UsersEmail, ProjectsID),
CONSTRAINT ProjectsID_FK FOREIGN KEY (ProjectsID) REFERENCES Projects (ProjectID)
ON DELETE CASCADE ON UPDATE CASCADE ,
CONSTRAINT UsersEmail_FK FOREIGN KEY (UsersEmail) REFERENCES Users(Email)
ON DELETE CASCADE ON UPDATE CASCADE
);
I am now trying to create a stored procedure that will insert a new project to the Projects table. After I add the project I want to create a reference to it in the ProjectsUsers table. The problem is, there is no possible way for me to know what the id of the project I just created - thus, I am unable to know what ID should I insert into the ProjectsUsers.
So if my stored procedure was something like this:
INSERT INTO Projects (Project, CreationDate, ProjectName)
VALUES (#project, GETDATE(), #email);
INSERT INTO ProjectsUsers VALUES (#email, ???)
How can I get the ID?
Just use SCOPE_IDENTITY like this:
INSERT INTO Projects (Project, CreationDate, ProjectName)
VALUES (#project, SYSDATETIME(), #email);
DECLARE #ProjectID INT = SCOPE_IDENTITY();
INSERT INTO ProjectsUsers
VALUES (#email, #ProjectID)
More all the relevant details about SCOPE_IDENTITY on the official Microsoft Documentation site.
As Sean Lange mentions, you can use SCOPE_IDENTITY to get last id inserted from within your proc
You can also use the OUTPUT clause and get possibly many ids. You can output in the screen or in a table, but it wont work if you are selecting from a table that has triggers.
Use the OUTPUT clause! Do not use the various identity functions or variables. This directly solves your problem:
DECLARE #ids TABLE (ProjectId int);
INSERT INTO Projects (Project, CreationDate, ProjectName)
OUTPUT inserted.ProjectId INTO #ids;
VALUES (#project, GETDATE(), #email);
INSERT INTO ProjectsUsers (UsersEmail, ProjectId)
SELECT #email, ProjectId
FROM #ids;
All the other methods of returning the identity have peculiarities:
Perhaps they don't work when the insert has multiple statements.
Perhaps concurrent inserts mess up the value.
Perhaps they don't work well with triggers.

Trigger ON Table which fire INSERT into another table which has NOT NULL constraint

CREATE TRIGGER logaction ON temployeelog
AFTER INSERT
AS
BEGIN
INSERT INTO TABLE temployee(ename, experience)
SELECT ename,experience FROM INSERTED
END
The structure of temployee
CREATE TABLE temployee
(
ename VARCHAR(20),
experience INT NOT NULL
)
ALTER TABLE temployeeADD DEFAULT (0) FOR experience
When I don't pass data in the experience column WHILE INSERT I get an error.
Cannot insert the value NULL into column 'experience', table
'temployee'; column does not allow nulls. INSERT fails. The statement
has been terminated.
I wanted to pass NULL Values temployeelog table AND wanted those situation to be handled by 'DEFAULT VALUES kept in temployee'
How can I achieve that?
The table default only comes into play if you don't insert it, so split the insert into one which handles a non-null experience and one which handles a null experience
INSERT INTO TABLE temployee (ename, experience)
SELECT ename, experience
FROM INSERTED
WHERE experience IS NOT NULL;
INSERT INTO TABLE temployee (ename)
SELECT ename
FROM INSERTED
WHERE experience IS NULL;

Inserting one table's primary key into another table

I have two tables in SQL Server, Appointment and AppointmentDetails.
Appointment table has two columns AppId and CusId.
AppointmentDetail table has AppId, ApDay, Intime, OutTime, EmpId.
Appointment table has AppId as its primary key, and it is set to auto increment.
AppointmentDetails table has primary key on (AppId, ApDay).
My problem how get and insert the primary key of the Appointment table to AppointmentDetails table when I am inserting data into Appointment table???
Here's one method of doing it (with corrected spellings for table names though I accept these are sometimes beyond your control):
DECLARE #insertedId INT;
BEGIN TRANSACTION
INSERT INTO Appointment(CusId) VALUES(#cusId);
SET #insertedId = SCOPE_IDENTITY();
COMMIT
BEGIN TRANSACTION
INSERT INTO
AppointmentDetails
SELECT
AppId = #insertedId
,ApDay = #apDay
,Intime = #inTime
,OutTime = #outTime
,EmpId = #empId
FROM
Appointment
COMMIT
Alternatively you could use a trigger, but they're just evil!
I think they asking how can they return the new key that was generated to then insert it into the details table.
Check this post out if using JDBC but the idea is the same for all languages: How to get the insert ID in JDBC?

How to delete Duplicates in MySQL table

I've given a client the following query to delete duplicate phone no. records in an MSSQL database, but now they need to also do it on MySQL, and they report that MySQL complains about the format of the query. I've included the setup of a test table with duplicates for my code sample, but the actual delete query is what counts.
I'm asking this in ignorance and urgency, as I am still busy downloading and installing MySQL, and just maybe somebody can help in the mean time.
create table bkPhone
(
phoneNo nvarchar(20),
firstName nvarchar(20),
lastName nvarchar(20)
)
GO
insert bkPhone values('0783313780','Brady','Kelly')
insert bkPhone values('0845319792','Mark','Smith')
insert bkPhone values('0834976958','Bill','Jones')
insert bkPhone values('0845319792','Mark','Smith')
insert bkPhone values('0828329792','Mickey','Mouse')
insert bkPhone values('0834976958','Bill','Jones')
alter table bkPhone add phoneId int identity
delete from bkPhone
where phoneId not in
(
select min(phoneId)
from bkPhone
group by phoneNo,firstName,lastName
having count(*) >= 1
)
Many ways lead to Rome. This is one. It is very fast. So you can use it with big databases. Don't forget the indeces.
The trick is: make phoneNo unique and use "ignore".
drop table if exists bkPhone_template;
create table bkPhone_template (
phoneNo varchar(20),
firstName varchar(20),
lastName varchar(20)
);
insert into bkPhone_template values('0783313780','Brady','Kelly');
insert into bkPhone_template values('0845319792','Mark','Smith');
insert into bkPhone_template values('0834976958','Bill','Jones');
insert into bkPhone_template values('0845319792','Mark','Smith');
insert into bkPhone_template values('0828329792','Mickey','Mouse');
insert into bkPhone_template values('0834976958','Bill','Jones');
drop table if exists bkPhone;
create table bkPhone like bkPhone_template;
alter table bkPhone add unique (phoneNo);
insert ignore into bkPhone (phoneNo,firstName,lastName) select phoneNo,firstName,lastName from bkPhone_template;
drop table bkPhone_template;
If the data table already exists, then you only have to run a create table select with a following insert ignore select. At the end you have to run some table renaming statements. That's all.
This workaround is much,much faster then a delete operation.
You can select out the unique ones by:
select distinct(phoneNo) from bkPhone
and put them into another table, delete the old table and rename the new one to the old name.
MySQL complains, because it makes no sense. You trying to aggregate using min() column by which you group.
Now, if you're trying to delete duplicate phone numbers for the same person, the SQL should be:
delete from bkPhone
where phoneId not in
(
select min(phoneId)
from bkPhone
group by firstName,lastName /* i.e. grouping by person and NOT grouping by phoneId */
having count(*) >= 1
)
Mysql also included:
http://mssql-to-postgresql.blogspot.com/2007/12/deleting-duplicates-in-postgresql-ms.html