Check CONSTRAINT Not work in SQL Server - sql

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

Related

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

Computed Columns in INSERT Statement

Created a table:
CREATE TABLE [dbo].[tblPacks]
(
[ID] [int] NOT NULL,
[BatchNumber] [varchar](30) NULL,
[PackID] AS (CONVERT([varchar](50), 'PK' + case when len([ID]) <= (3) then CONVERT([varchar](20), right((0.001) * [ID], (3)), 0) else CONVERT([varchar](20), [ID], 0) end, 0)) PERSISTED,
[Status] [int] NULL
)
Now I Had a New Requirement that Other than this sequence also can be added into this table So that I Will Remove Computed Logic and Create Table As:
CREATE TABLE [dbo].[tblPacks]
(
[ID] [int] NOT NULL,
[BatchNumber] [varchar](30) NULL,
[PackID] VARCHAR(50),
[Status] [int] NULL
)
Now I Want Two Insert Statements which 'PackID' Should have logic Which is Similar to this:
AS (CONVERT([varchar](50),'PK'+case when len([ID])<=(3) then
CONVERT([varchar](20),right((0.001)*[ID],(3)),0) else
CONVERT([varchar](20),[ID],0) end,0))
And A Normal Insert statement which has no logic How can I DO that?
Since your ID column is not an identity column, you can simply use the statement in your computed column with your ID value (for better readability I assume you have a parameter #ID for your ID value):
INSERT INTO tblPacks (ID, PackID)
VALUES (
#ID,
CONVERT([varchar](50),'PK'+case when len(#ID)<=(3) then CONVERT([varchar](20),right((0.001)*#ID,(3)),0) else CONVERT([varchar](20),#ID,0) end,0)
)
If your ID column actually is an identity column, you need SCOPE_IDENTITY() to get your inserted ID value. However, since SCOPE_IDENTITY() only gives you the ID after the value has been inserted, you need two steps. It is a good idea to use a transaction for these steps to make your operation atomic:
BEGIN TRAN
INSERT INTO tblPacks (ID) VALUES (#ID)
UPDATE tblPacks
SET PackID = CONVERT([varchar](50),'PK'+case when len(SCOPE_IDENTITY())<=(3) then CONVERT([varchar](20),right((0.001)*SCOPE_IDENTITY(),(3)),0) else CONVERT([varchar](20),SCOPE_IDENTITY(),0) end,0)
WHERE ID = SCOPE_IDENTITY()
COMMIT
You can also use a trigger on your table instead of a computed column that sets the value if you have not set it before:
CREATE TRIGGER TR_tblPacks ON tblPacks
AFTER INSERT
AS
UPDATE tblPacks
SET PackID = CONVERT([varchar](50),'PK'+case when len(inserted.ID)<=(3) then CONVERT([varchar](20),right((0.001)*inserted.ID,(3)),0) else CONVERT([varchar](20),inserted.ID,0) end,0)
FROM inserted
WHERE tblPacks.ID = inserted.ID AND tblPacks.PackID IS NULL
With this trigger you only add your custom value to the INSERT statement if you have it. If you don't have it, the trigger will set the default value.
You could set user defined function as computed column.like this..
ALTER TABLE TableName ADD YourColumn AS dbo.TestFunction(otherColumn);
then, you can include all your logic for this computed column in that user defined function.

Constraint violation on parallel run

I created a function that will serves as primary key for my table
CREATE FUNCTION dbo.NewCustomerPK()
RETURNS VARCHAR (10)
AS
BEGIN
DECLARE #LastCustID VARCHAR(10)
DECLARE #newID INT
DECLARE #charID CHAR(10)
SELECT
#LastCustID = MAX(CustID)
FROM
dbo.TestCust
IF (#LastCustID IS NULL)
BEGIN
SET #LastCustID = 'CUST000001'
END
ELSE
BEGIN
SET #newID = RIGHT(#LastCustID, 6) + 1
SET #charID = 'CUST' + RIGHT(('0000000' + CONVERT(VARCHAR(6), #newID)), 6)
SET #LastCustID = #charID
END
RETURN #LastCustID
END
CREATE TABLE dbo.TestCust
(
CustID VARCHAR(10) PRIMARY KEY NOT NULL DEFAULT dbo.NewCustomerPK(),
Name VARCHAR(50)
)
And tried to insert a test data
DECLARE #Counter INT = 1,
#Stopper INT = 500000
WHILE(#Counter <= #Stopper)
BEGIN
INSERT INTO dbo.TestCust(NAME)
VALUES('test'+CONVERT(VARCHAR(6), #Counter))
SET #Counter = #Counter + 1
END
It works fine but when I try a parallel run(Running the loop data insertion in the new window) it cause a Primary Constraint Violation Error
Another way to do this is to use an identity column in combination with a calculated column.
create table dbo.TestCust
(
ID int identity not null,
CustID as isnull('CUST'+right('0000000' + convert(varchar(6), ID), 6), ''),
Name varchar(50),
constraint PK_TestCust_CustID primary key clustered (CustID)
);
The isnull around the calculation of CustID is there to make sure the column will never have NULL values and that makes it possible to use CustID as a primary key.
Not sure if it is possible to fix your current design. Perhaps using isolation level serializable when adding new rows would do the trick.
CREATE TRIGGER tr_Group ON TargetTable
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON
INSERT INTO dbo.targetTable
SELECT dbo.NewCustomerPK(),<other fields>
FROM INSERTED
END

optimal solution for lookup on large sql server table

I have a large table of user ids and another table of user records which contains a user post with user ids. The process is whenever a new feed post is retrieved,I do a request to the user id table for an id that is marked inactive ( I have that field ACTIVE because I have another process that creates these ids and inserts it continuously into table 1) and when an id is requested it is marked as inactive.
Then I check if the user exists in the user table(table 2) and if so return the user id associated with that user.
I was told that I can speed up this process but creating a hash table to do the lookup on table 2. I am not sure how to even start this and any links or samples will be appreciated.
Also I need to run a separate process that cleans table 1 and removes all inactive user ids.
When I call the procedure to insert into table 2, I pass the user id retrieved from table 1.
CREATE TABLE [dbo].[userforums]
(
[userid] [VARCHAR](16) NOT NULL CONSTRAINT [PK_forumssiteid] PRIMARY KEY CLUSTERED ,
[forumname] [VARCHAR](500) NOT NULL,
[exported] [INT] NULL,
[lastcrawled] [DATETIME] NULL,
[priority] [INT] NULL,
[origin] [VARCHAR](50) NULL,
[queryid] [VARCHAR](25) NULL,
[dateinserted] [DATETIME] NULL DEFAULT (getdate())
)
second table
CREATE TABLE [dbo].[userids]
(
[userid] [NVARCHAR](20) NOT NULL CONSTRAINT [PK_userids] PRIMARY KEY CLUSTERED,
[active] [NVARCHAR](20) NULL CONSTRAINT [IX_userids] UNIQUE NONCLUSTERED
)
get user id stored procedure
BEGIN TRANSACTION
SELECT TOP 1 #id = userid
FROM userids WITH (UPDLOCK, HOLDLOCK)
WHERE active = 'Y'
OR active IS NULL
UPDATE userids
SET active = 'N'
WHERE userid = #id
COMMIT TRANSACTION
check if userid exists
CREATE PROC Foo #forumname VARCHAR(500),
#userid VARCHAR(16),
#origin VARCHAR(50),
#queryid VARCHAR(25)
AS
SET NOCOUNT ON;
DECLARE #cnt INT
DECLARE #serverip VARCHAR(16)
DECLARE #mincnt INT
DECLARE #siteservercnt INT
SELECT #cnt = COUNT(*)
FROM userforums
WHERE forumname = #forumname
IF #cnt = 0
BEGIN
INSERT INTO userforums
(forumname,
userid,
exported,
origin,
queryid)
VALUES (#forumname,
#userid,
1,
#origin,
#queryid)
SELECT #siteservercnt = COUNT(*)
FROM siteserverip
WHERE userid = #userid
IF #siteservercnt = 0
BEGIN
SELECT TOP 1 #mincnt = COUNT(*),
#serverip = serverip
FROM siteserverip
GROUP BY serverip
ORDER BY COUNT(*)
SELECT TOP 1 #mincnt = sitecount,
#serverip = serverip
FROM serveripcounts
ORDER BY sitecount
INSERT INTO siteserverip
VALUES (#siteid,
#serverip)
UPDATE serveripcounts
SET sitecount = sitecount + 1
WHERE serverip = #serverip
END
END
SELECT userid
FROM userforums
WHERE forumname = #forumname
RETURN
Your existing dequeue query can be improved. Instead of
DECLARE #id INT
SELECT TOP 1 #id = userid
FROM userids WITH (UPDLOCK, HOLDLOCK)
WHERE active = 'Y'
OR active IS NULL
UPDATE userids
SET active = 'N'
WHERE userid = #id
Which is two operations (a clustered index scan followed by an index seek) you can do
UPDATE TOP (1) userids
WITH (ROWLOCK, READPAST)
SET active = 'N'
OUTPUT INSERTED.userid
WHERE active <> 'N'
Which is one operation and gives a plan with two range seeks.
A Hash table #TableName is a temporary object in tempdb that functions as a table. They are generally called 'temp tables'. I would NOT be using them as a first solution for retrieving data on the fly if this is a common occurrence. Instead I would create an index and see if that justifies your needs. Generally hash tables are used for intense operations where you want to get a set of things that may or may not be indexed and then relate it to something else and you want to keep it in memory.
I would create an index and that should improve speed. Also if you find is slow, a hash table won't speed that part up, it will just be putting a collection of that into a source to reuse seperated from the main table.
create index IX_[yourtableName]_[TableColumn(s)] on [Tablename]([Column(s)]
I would not create more objects unless necessary. Generally if your UserId's are valid ints you can search on them quite fast.

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