if condition, then upsert else do nothing - sql

I am trying to upsert a value only if a condition is true, otherwise I dont want to do anything to the record. My code seems to generate primary key errors when I run it again (indicating the insert is firing, but the whole statement should be skipped)
Error on second run: Violation of PRIMARY KEY constraint 'PK__chicken__A2D9E564407C90CD'. Cannot insert duplicate key in object 'dbo.chicken'. The duplicate key value is (10118-78843).
Query:
IF((select status from chicken where [PrimaryKey]='10118-78843')!='paid')
update [eComm]..[chicken] set [Status]='Overdue' where [PrimaryKey]='10118-78843'
IF ##ROWCOUNT=0 insert into [eComm]..[chicken]([PrimaryKey],[Status]) values('10118-78843','Paid');
create example table code:
create table chicken(
PrimaryKey nvarchar(50) not null,
Status nvarchar(10),
PRIMARY KEY (PrimaryKey)
)
Goal (this is invalid syntax):
IF((select status from chicken where [PrimaryKey]='10118-78843')!='paid')
{
update [eComm]..[chicken] set [Status]='Overdue' where [PrimaryKey]='10118-78843'
IF ##ROWCOUNT=0 insert into [eComm]..[chicken]([PrimaryKey],[Status]) values('10118-78843','Paid');
}
else, do nothing

The problem is that you are not taking into account what happens if there is an existing row but the status is paid.
You need to tweak the logic a bit:
declare #status varchar(20) = (
select c.status
from chicken c
where c.[PrimaryKey] = '10118-78843'
);
if (#status != 'paid')
update [chicken]
set [Status] = 'Overdue'
where [PrimaryKey] = '10118-78843';
else if (#status is null)
insert into [chicken] ([PrimaryKey], [Status])
values ('10118-78843', 'Paid');
Alternatively:
insert into [chicken] ([PrimaryKey], [Status])
select '10118-78843', 'Paid'
where not exists (select 1
from chicken c
where c.[PrimaryKey] = '10118-78843'
);
if (##ROWCOUNT = 0)
update [chicken]
set [Status] = 'Overdue'
where [PrimaryKey] = '10118-78843'
and status != 'Paid';

Related

How to prevent insert when condition is met

I have a two tables
CREATE TABLE table1 (
ID int not null,
Status int not null,
TimeStamp datetime2(3)
CONSTRAINT pkId PRIMARY KEY (ID)
);
and
CREATE TABLE Status (
StatusID int not null,
StatusName varchar(64) not null
CONSTRAINT pStatus PRIMARY KEY (StatusID)
);
with inserted values
INSERT INTO table1 (ID,StatusID,TimeStamp) VALUES
(1,1,'2021-06-15 07:30:31'),
(2,2,'2021-07-15 07:30:31'),
(3,3,'2021-08-15 07:30:31'),
(4,4,'2021-09-15 08:30:31'),
(5,5,'2021-09-15 07:30:31'),
(6,5,'2021-09-15 07:30:31'),
(7,4,'2021-09-15 07:30:31'),
(8,2,'2021-09-15 07:30:31'),
(9,1,'2021-09-15 07:30:31');
and
INSERT INTO dbo.Status (StatusID,StatusName) VALUES
(1,'wants to enroll'),
(2,'granted enrollment'),
(3,'declined enrollment'),
(4,'attending the course'),
(5,'finished the course');
I wrote a trigger but it prevent all insert when the StatusID < 4 and what I want is to prevent insert into TimeStamp only when the condition is StatusID < 4. I use SQL Server.
CREATE TRIGGER dbo.Prevent_Insert4
ON dbo.table1
FOR INSERT
AS
SET NOCOUNT ON
IF EXISTS(SELECT *
FROM dbo.table1 t
JOIN inserted i ON t.ID=i.ID
WHERE t.StatusID<4)
BEGIN
RAISERROR('You can not update when StatusId value < 4', 16, 1)
ROLLBACK TRAN
SET NOCOUNT OFF
RETURN
END
SET NOCOUNT OFF
Thank you in advance.
Sounds like you just need a CHECK constraint
ALTER TABLE table1
ADD CONSTRAINT CHK_Status
CHECK(Status >= 4 OR TimeStamp IS NULL);
If you really, really wanted to use triggers for this (not recommended) then it should look like this
CREATE OR ALTER TRIGGER dbo.Prevent_Insert4
ON dbo.table1
FOR INSERT, UPDATE
AS
SET NOCOUNT ON;
IF EXISTS(SELECT 1
FROM inserted i
WHERE i.StatusID < 4
AND i.TimeStamp IS NOT NULL)
BEGIN
THROW 50001, 'You can not set TimeStamp when StatusId value < 4', 1;
-- no need for rollback, will happen automatically if you use THROW
END;
go
With your code you are doing rollback of inserted row when condition is met. If I understood you correctly you just to want override timestamp with null value if StatusId < 4.
If so, you should write that instead of rollback
IF EXISTS(SELECT *
FROM dbo.table1 t
JOIN inserted i ON t.ID=i.ID
WHERE t.StatusID<4)
BEGIN
UPDATE t SET t.TimeStamp = NULL
FROM inserted i
JOIN dbo.table1 t ON i.ID = t.ID
WHERE t.StatusID<4
END

INSERT statement conflicting with FOREIGN KEY constraint, but there's no conflict apparent

I'm not sure why I'm getting this error, because no data is being inserted into the column mentioned in the error. It's a PK field set to IDENTITY, so the value is auto-filled with each record added. The function is supposed to insert a record into an audit table, called by a trigger on the source table. Here's the error and my code.
Msg 547, Level 16, State 0, Procedure tblTriggerAuditRecord_TTeamPlayers, Line 33 [Batch Start Line 344]
The INSERT statement conflicted with the FOREIGN KEY constraint "Z_TTeamPlayers_TTeams_FK". The conflict occurred in database "dbSQL1", table "dbo.Z_TTeams", column 'intTeamAuditID'.
--Problematic Code
DELETE FROM TTeamPlayers
DELETE FROM TTeams
WHERE strTeam = 'Reds'
SELECT * FROM TTeams
SELECT * FROM Z_TTeams
=========================
-- both these tables are updated when a DELETE is run. I have the FK constraint set to CASCADE so that when it's deleted out of the child table Z_TTeamPlayers, the parent record is deleted.
CREATE TABLE Z_TTeamPlayers
(
intTeamPlayerAuditID INTEGER IDENTITY NOT NULL
,intTeamAuditID INTEGER NOT NULL
,intPlayerAuditID INTEGER NOT NULL
,UpdatedBy VARCHAR(50) NOT NULL
,UpdatedOn DATETIME NOT NULL
,strAction VARCHAR(10) NOT NULL
,strModified_Reason VARCHAR(1000)
--,CONSTRAINT PlayerTeam_UQ UNIQUE ( intTeamID, intPlayerID )
,CONSTRAINT Z_TTeamPlayers_PK PRIMARY KEY ( intTeamPlayerAuditID )
)
CREATE TABLE Z_TTeams
(
intTeamAuditID INTEGER IDENTITY NOT NULL
,intTeamID INTEGER NOT NULL
,strTeam VARCHAR(50) NOT NULL
,strMascot VARCHAR(50) NOT NULL
,UpdatedBy VARCHAR(50) NOT NULL
,UpdatedOn DATETIME NOT NULL
,strAction VARCHAR(10) NOT NULL
,strModified_Reason VARCHAR(1000)
,CONSTRAINT Z_TTeams_PK PRIMARY KEY ( intTeamAuditID )
)
==============================
ALTER TABLE Z_TTeamPlayers ADD CONSTRAINT Z_TTeamPlayers_TTeams_FK
FOREIGN KEY ( intTeamAuditID ) REFERENCES Z_TTeams ( intTeamAuditID ) ON DELETE CASCADE
==============================
-- --------------------------------------------------------------------------------
-- Create Trigger for Z_TTeamPlayers
-- --------------------------------------------------------------------------------
GO
CREATE TRIGGER tblTriggerAuditRecord_TTeamPlayers on TTeamPlayers
AFTER UPDATE, INSERT, DELETE
AS
DECLARE #Now DATETIME
DECLARE #Modified_Reason VARCHAR(1000)
DECLARE #Action VARCHAR(10)
SET #Action = ''
-- Defining if it's an UPDATE, INSERT, or DELETE
BEGIN
IF (SELECT COUNT(*) FROM INSERTED) > 0
IF (SELECT COUNT(*) FROM DELETED) > 0
SET #Action = 'UPDATE'
ELSE
SET #Action = 'INSERT'
ELSE
SET #Action = 'DELETE'
END
SET #Now = GETDATE() -- Gets current date/time
IF (#Action = 'INSERT')
BEGIN -- Begin INSERT info
INSERT INTO Z_TTeamPlayers(intTeamAuditID, intPlayerAuditID, UpdatedBy, UpdatedOn, strAction, strModified_Reason)
SELECT I.intTeamID, I.intPlayerID, SUSER_NAME(), GETDATE(), #Action, I.strModified_Reason
FROM INSERTED as I
INNER JOIN TTeamPlayers as T ON T.intTeamPlayerID = I.intTeamPlayerID
END -- End Insert Info
ELSE
IF (#Action = 'DELETE')
BEGIN -- Begin INSERT info
INSERT INTO Z_TTeamPlayers(intTeamAuditID, intPlayerAuditID, UpdatedBy, UpdatedOn, strAction, strModified_Reason)
SELECT D.intTeamID, D.intPlayerID, SUSER_SNAME(), GETDATE(), #Action, ''
FROM DELETED as D
END -- End Delete Info
ELSE -- #Action = 'UPDATE'
BEGIN --begin UPDATE info get modified reason
IF EXISTS (SELECT TOP 1 I.strModified_Reason FROM INSERTED as I, TPlayers as T WHERE I.intPlayerID = T.intPlayerID
AND I.strModified_Reason <> '')
BEGIN -- beging insert of UPDATE info
INSERT INTO Z_TTeamPlayers(intTeamAuditID, intPlayerAuditID, UpdatedBy, UpdatedOn, strAction, strModified_Reason)
SELECT I.intTeamID, I.intPlayerID, SUSER_SNAME(), GETDATE(), #Action, I.strModified_Reason
FROM TTeamPlayers as T
INNER JOIN INSERTED as I ON T.intPlayerID = I.intPlayerID
-- set modified reason column back to empty string
UPDATE TPlayers SET strModified_Reason = NULL
WHERE intPlayerID IN (SELECT TOP 1 intPlayerID FROM INSERTED)
END
ELSE
BEGIN -- begin if no modified reasson supplied
PRINT 'Error and rolled back, please enter modified reason'
ROLLBACK
END
END
z_TTeamPlayers.intTeamAuditID references your audit table's primary key. In your code you are inserting that value into z_TTeamPlayers... INSERT INTO Z_TTeamPlayers(intTeamAuditID... while it doesn't exist (as a primary key) in your audit table yet... thus it fails.
Here is a demo.
I get that you are trying to audit, but i'm not sure your business logic on teams and players. You seem to have your design a bit backwards. You could always use versioning in SQL Server.
As a guess you probably want a design similar to this instead

How to declare in stored procedure that a row does not exist?

I want to know if a certain QuestionID and EmployeeID exist; if they do, they need to be inserted like here below (that's working fine).
But if they don't exist, I want a good error for the user, so that he knows that the QuestionID or the EmployeeID or both do not exist. Also maybe a rollback of the transaction? Now I can add every number, but the stored procedure still completed the commands...
I have this code (I'm using SQL Server):
CREATE PROCEDURE Contentment
#employeeID INT,
#questionid INT
AS
BEGIN
IF EXISTS (SELECT questionid
FROM question
WHERE questionid = #questionid)
IF EXISTS (SELECT employeeID
FROM employee
WHERE employeeid = #employeeID)
BEGIN
INSERT INTO contentment (employeeid, questionid, date, score, comment)
VALUES (#employeeID, #questionid, null, null, null)
END
ELSE
IF #employeeID = 0
RAISERROR ('-certain error-', 16, 1, #employeeid)
IF #questionid = 0
RAISERROR ('-certain error-', 16, 1, #questionid)
END
I'd try to do it like this - check for the existence of both parameters in a single statement - if not, then provide an error:
CREATE PROCEDURE dbo.Contentment
#employeeID INT,
#questionid INT
AS
BEGIN
-- check for *both* conditions at once
IF EXISTS (SELECT * FROM dbo.question WHERE questionid = #questionid) AND
EXISTS (SELECT * FROM dbo.employee WHERE employeeid = #employeeID)
BEGIN
-- if OK --> insert
INSERT INTO dbo.contentment (employeeid, questionid, date, score, comment)
VALUES (#employeeID, #questionid, null, null, null)
END
ELSE
BEGIN
-- if NOT OK --> provide error
RAISERROR ('#EmployeeID or #QuestionID do not exist', 16, 1, null)
END
END
Also maybe a rollback of the transaction?
Since you're not really doing anything (no INSERT happening in either of the two ID's doesn't exist), there's really no transaction to roll back at this point....
you can use an error message parameter like that :
CREATE PROC dbo.Usp_Contentment
AS
begin
declare #ErrMsg NVARCHAR(MAX) = ''
IF not exists (select 1 from dbo.Question where QuestionId= #QuestionId)
set #ErrMsg = 'Some msg'
if not exists (select 1 from dbo.Employee where Employee Id= #EmployeeId)
set #ErrMsg =#ErrMsg + 'some msg'
IF #msg=''
INSERT INTO dbo.contentment (employeeid, questionid, date, score, comment)
VALUES (#employeeID, #questionid, null, null, null)
else
RAISERROR(#ErrMsg,16,1)
end
don't use ROLLBACK because you handled the NULL Values
I would let the database do the work. Declare the table to have proper foreign key relationships:
alter table contentment add constraint fk_conententment_question
foreign key (questionid) references question(questionid);
alter table contentment add constraint fk_conententment_employee
foreign key (employeeid) references employee(employeeid);
Then write the procedure as:
CREATE PROCEDURE dbo.usp_Contentment (
#employeeID INT,
#questionid INT
) AS
BEGIN
INSERT INTO dbo.contentment (employeeid, questionid, date, score, comment)
VALUES (#employeeID, #questionid, null, null, null)
END; -- usp_Contentment
The database will generate an error that the foreign key relationship fails -- if there is no matching key.
This approach is much better than doing the test yourself for several reasons:
The database ensures the data integrity, so you know the relationships are maintained regardless of where the data changes occur.
The integrity applies to both inserts and updates. There is no need to put checks in multiple places.
The integrity check is thread-safe, so if rows are being modified in the reference tables, the integrity check works.
If you really want a customized message, you can catch the error in a try/catch block and have a customized error.

Check CONSTRAINT Not work in SQL Server

I have to restrict to insert duplicate data in my table with condition
Here is SQL Server Table
CREATE TABLE [dbo].[temptbl](
[id] [numeric](18, 0) IDENTITY(1,1) NOT NULL,
[DSGN] [varchar](500) NULL,
[RecordType] [varchar](1000) NULL
)
I want to put condition on RecordType, if RecordType is 'SA' Than check CONSTRAINT (means if DSGN = 0 and RecordType = 'SA' Exist than i don't want to insert that data.
if DSGN = 1 and RecordType = 'SA' Not Exist than i want to insert that data.
If RecordType is other than 'SA' than insert any data
For that i create constraint but it is not work
ALTER TABLE temptbl WITH CHECK ADD CONSTRAINT chk_Stuff CHECK (([dbo].[chk_Ints]([DSGN],[RecordType])=(0)))
GO
ALTER FUNCTION [dbo].[chk_Ints](#Int_1 int,#Int_2 varchar(20))
RETURNS int
AS
BEGIN
DECLARE #Result INT
BEGIN
IF #Int_2 = 'SA'
BEGIN
IF NOT EXISTS (SELECT * FROM [temptbl] WHERE DSGN = #Int_1 AND RecordType = #Int_2)
BEGIN
SET #Result = 0
END
ELSE
BEGIN
SET #Result = 1
END
END
ELSE
BEGIN
SET #Result = 0
END
END
RETURN #Result
END
But it is not working. please suggest me
Ditch the function and the check constraint:
CREATE UNIQUE INDEX IX_temptbl_SA ON temptbl (DSGN) WHERE RecordType='SA'
This is known as a filtered index.
Your check constraint wasn't working as you thought it would because when a check constraint is evaluated for any particular row, that row is already visible within the table (within the context of that transaction) and so each row was effectively blocking its own insertion.
Use With NOCheck for only new data. Here is the link provided.
https://technet.microsoft.com/en-us/library/ms179491(v=sql.105).aspx

How to check for duplicate records in table while inserting and updating

I need to check for duplicate records in my table while insert and update. Below is my query. It works fine in case of insert, but fails at update. I am unable to figure that out. Please help me.
Insert:
Set #Count = (Select ISNULL(Count(*),0) From AccountMaster Where [Name] = #Name and Id=#Id and Flag=1);
If #Count >0 and #InsertUpdateFlag=1
BEGIN
Set #DbErrormessage = 'Business Name and Account Id must be unique. Try again.';
END
Update :
Set #Count = (Select Count(*) From AccountMaster Where [Name] = #Name and Id <> #Id and Flag=1);
If #Count >0 and #InsertUpdateFlag=2
BEGIN
Set #DbErrormessage = 'Business Name is in already in use by some other account. Try another one.';
END
The condition of update is that the Id and Name must not exist in the database. So I am taking count where name doesnot exists with any other id. But it seems , not working.
Below is my table schema.
SELECT [PkId] //Primary Key as Int
,[Id] // Unique Key varchar(25)
,[Created]
,[Type]
,[Status]
,[Name] //Business Name
,[ContactPerson]
,[ContactNumber]
,[Email]
,[Address]
,[LocationId]
,[Remarks]
,[Flag]
FROM [AccountMaster]
Id and Business Name must be unique while both insert/update. So I need to check it while insert and update.
Put the Unique constraint in the name and forget the checks, you can handle the Constraint violation and then you know the name already exists