The DELETE statement conflicted with the REFERENCE constraint "FK_BlogCommentReply_BlogCommentId" - sql

I have a blog and keeping it simple for discussion, the main tables are Blog, BlogComment, BlogCommentReply and User.
A blog belongs to a User.
A comment belongs to a blog/user.
A comment reply belongs to a blog/comment/user.
I want to delete a User - UserId = 5.
My delete user stored procedure deletes (wrapped in a transaction) in this order:
BlogCommentReply
BlogComment
Blog (if the user is the author of the blog only)
User
It deletes the entry(s) in the BlogCommentReply for the User, then attempts to delete the entry(s) in the BlogComment for the User.
I then get the error:
The DELETE statement conflicted with the REFERENCE constraint "FK_BlogCommentReply_BlogCommentId".
The conflict occurred in database "DBGbngDev", table "dbo.BlogCommentReply", column
'BlogCommentId'.
It still thinks there is an entry in the BlogCommentReply table.
Here is the execute of the stored procedure with debug selects in it to show that it deletes the BlogCommentReply entry but then fails on the delete of the BlogComment.
Table definitions:
----------------------- BlogComment table (parent to blogCommentReply)
CREATE TABLE [dbo].[BlogComment](
[BlogCommentId] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] NOT NULL,
[BlogId] [int] NOT NULL,
[BlogCommentContent] [varchar](max) NOT NULL,
[LikeCount] [int] NOT NULL,
[DisLikeCount] [int] NOT NULL,
[DateTimeOfBlogComment] [datetime] NOT NULL,
CONSTRAINT [PK_BlogComment] PRIMARY KEY CLUSTERED
(
[BlogCommentId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[BlogComment] WITH CHECK ADD CONSTRAINT [FK_BlogComment_BlogId] FOREIGN
KEY([BlogId])
REFERENCES [dbo].[Blog] ([BlogId])
GO
ALTER TABLE [dbo].[BlogComment] CHECK CONSTRAINT [FK_BlogComment_BlogId]
GO
ALTER TABLE [dbo].[BlogComment] WITH CHECK ADD CONSTRAINT [FK_BlogComment_UserId] FOREIGN
KEY([UserId])
REFERENCES [dbo].[User] ([UserId])
GO
ALTER TABLE [dbo].[BlogComment] CHECK CONSTRAINT [FK_BlogComment_UserId]
GO
----------------------- BlogCommentReply table (child)
CREATE TABLE [dbo].[BlogCommentReply](
[BlogCommentReplyId] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] NOT NULL,
[BlogCommentId] [int] NOT NULL,
[BlogId] [int] NOT NULL,
[BlogCommentReplyContent] [varchar](max) NOT NULL,
[LikeCount] [int] NOT NULL,
[DisLikeCount] [int] NOT NULL,
[DateTimeOfBlogCommentReply] [datetime] NOT NULL,
CONSTRAINT [PK_BlogCommentReply] PRIMARY KEY CLUSTERED
(
[BlogCommentReplyId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[BlogCommentReply] WITH CHECK ADD CONSTRAINT [FK_BlogCommentReply_BlogCommentId]
FOREIGN KEY([BlogCommentId])
REFERENCES [dbo].[BlogComment] ([BlogCommentId])
GO
ALTER TABLE [dbo].[BlogCommentReply] CHECK CONSTRAINT [FK_BlogCommentReply_BlogCommentId]
GO
ALTER TABLE [dbo].[BlogCommentReply] WITH CHECK ADD CONSTRAINT [FK_BlogCommentReply_BlogId] FOREIGN
KEY([BlogId])
REFERENCES [dbo].[Blog] ([BlogId])
GO
ALTER TABLE [dbo].[BlogCommentReply] CHECK CONSTRAINT [FK_BlogCommentReply_BlogId]
GO
ALTER TABLE [dbo].[BlogCommentReply] WITH CHECK ADD CONSTRAINT [FK_BlogCommentReply_UserId] FOREIGN
KEY([UserId])
REFERENCES [dbo].[User] ([UserId])
GO
ALTER TABLE [dbo].[BlogCommentReply] CHECK CONSTRAINT [FK_BlogCommentReply_UserId]
GO
Extracted parts of the delete stored procedure (#a_UserId = 5):
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- Step 6. Get the number of blog comments replys for the user.
-- Note: there may not be any.
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SELECT #BlogCommentReplysCount = Count(*)
FROM dbo.BlogCommentReply
WHERE ( UserId = #a_UserId )
SELECT #ReturnCode = ##ERROR
IF #ReturnCode <> 0
BEGIN
ROLLBACK TRANSACTION
SELECT #Message = 'Critical Error - procedure DeleteBlog failed during the select of BlogCommentReply. User id: ' + CAST(#a_UserId AS VARCHAR) + '. '
RAISERROR (#Message, 16, 1)
END
SELECT '#BlogCommentReplysCount: ' + CAST(#BlogCommentReplysCount AS VARCHAR) + '. '
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- Step 7. Delete the BlogCommentReply entries for the user.
-- Note: the user may not have made any.
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DELETE dbo.BlogCommentReply
WHERE ( UserId = #a_UserId )
SELECT #ReturnCode = ##ERROR,
#RowCount = ##ROWCOUNT
IF #ReturnCode <> 0 AND #RowCount > 0
BEGIN
ROLLBACK TRANSACTION
SELECT #Message = 'Critical Error - procedure DeleteBlog failed during the delete of BlogCommentReply. User id: ' + CAST(#a_UserId AS VARCHAR) + '. '
RAISERROR (#Message, 16, 1)
END
SELECT 'Deleted BlogCommentReply'
SELECT '#ReturnCode: ' + CAST(#ReturnCode AS VARCHAR) + '. '
SELECT '#RowCount: ' + CAST(#RowCount AS VARCHAR) + '. '
SELECT '#a_UserId: ' + CAST(#a_UserId AS VARCHAR) + '. '
SELECT Count(*) as AFTERDELETE
FROM dbo.BlogCommentReply
WHERE ( UserId = #a_UserId )
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- Step 8. Get the number of blog comments for this user.
-- Note: there may not be any.
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SELECT #BlogCommentsCount = Count(*)
FROM dbo.BlogComment
WHERE ( UserId = #a_UserId )
SELECT #ReturnCode = ##ERROR
IF #ReturnCode <> 0
BEGIN
ROLLBACK TRANSACTION
SELECT #Message = 'Critical Error - procedure DeleteBlog failed during the select of BlogComment. User id: ' + CAST(#a_UserId AS VARCHAR) + '. '
RAISERROR (#Message, 16, 1)
END
SELECT '#BlogCommentsCount: ' + CAST(#BlogCommentsCount AS VARCHAR) + '. '
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- Step 9. Delete the BlogComment entries for the user.
-- Note: the user may not have made any.
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SELECT Count(*) as BEFORE
FROM dbo.BlogComment
WHERE ( UserId = #a_UserId )
SELECT 'Going to delete BlogComment'
DELETE dbo.BlogComment
WHERE ( UserId = #a_UserId )
SELECT #ReturnCode = ##ERROR,
#RowCount = ##ROWCOUNT
IF #ReturnCode <> 0 AND #RowCount > 0
BEGIN
ROLLBACK TRANSACTION
SELECT #Message = 'Critical Error - procedure DeleteBlog failed during the delete of BlogComment. User id: ' + CAST(#a_UserId AS VARCHAR) + '. '
RAISERROR (#Message, 16, 1)
END
SELECT 'Deleted BlogComment'

I think it's better to have your complete stored procedure script to check the error, but for the mean time I guess the problem is here:
DELETE dbo.BlogCommentReply
WHERE ( UserId = #a_UserId )
Ok, it's deleting all of comment replies which the related user (i.e. #a_UserId = 5) has.
But look at the rest of your script:
DELETE dbo.BlogComment
WHERE ( UserId = #a_UserId )
It's deleting all of user's comments, WHILE they may have some other comment replies for another users and they are not deleted. So I think the problem is here.
Maybe it's better to change the first deletion part to:
DELETE dbo.BlogCommentReply
WHERE ( UserId = #a_UserId )
DELETE dbo.BlogCommentReply
FROM [dbo].[BlogComment]
WHERE dbo.BlogCommentReply.BlogCommentId=[dbo].[BlogComment].BlogCommentId
AND [dbo].[BlogComment].UserId=#a_UserId

It's very simple you are deleting comment's reply of a specific user while other users might have reply under the same comment.
I you really want to delete all related info of a user , you have to go with different approach like finding all replies under that user's comment and delete those.

Related

In Micrsoft SQL is an UPDLOCK hint sufficient to avoid deadlocks in an SP that either inserts if not exists or updates otherwise?

I have a C# program with multiple threads that calls an SP to get a unique incrementing value.
I'm trying to prevent deadlocks using query hints UPDLOCK and HOLDLOCK.
The SP
Selects value from a table,
insert if the value is not found (initial value is 1)
Or updates the table by incrementing the value from the first select
Returns the incremented value in an output parameter.
Here the table
CREATE TABLE MyCounters(
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Key] [nvarchar](20) NOT NULL UNIQUE,
[Counter] [bigint] NOT NULL,
CONSTRAINT [PK_Table_1] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
Questions
Will a single UPDLOCK be sufficient in the first select to prevent deadlocks?
Or do I need to select with UPDLOCK and HOLDLOCK?
Or do I need a second dummy select with UPDLOCK and HOLDLOCK to only lock the entire table for inserts but not for updates?
I've read this question but there no insert is used.
Below the simplified SP and after that a script I used to test in 2 tabs in SQL management studio. With only a single UPDLOCK the test script works (i.e. no deadlocks and counters at the correct values).
BEGIN TRANSACTION
DECLARE #Key nvarchar(3)
SET #Key = 'AA'
DECLARE #counter bigint
SET #counter=NULL
-- *** Question 1 *** Will a single UPDLOCK be sufficient in the first select to prevent deadlocks?
-- *** Question 2 *** 1. Or do I need to select with UPDLOCK and HOLDLOCK?
SELECT #counter=Counter FROM MyCounters WITH (UPDLOCK) WHERE Key = #Key
IF (#counter IS NULL)
BEGIN
-- *** Question 3 *** Or do I need a second dummy select with UPDLOCK and HOLDLOCK to only lock the entire table for inserts but not update?
-- lock the entire table before inserting (select into variable)
--DECLARE #temp bigint
--SELECT TOP 1 #temp = Counter FROM MyCounters WITH (UPDLOCK, HOLDLOCK)
SET #counter=1
INSERT INTO MyCounters ([Key],Counter) VALUES (#Key, #counter)
END
ELSE
BEGIN
SET #counter=#counter+1
UPDATE MyCounters SET Counter = #counter WHERE Key = #Key
END
SELECT #Key, #counter
COMMIT
Below the test script (which I ran in multiple tabs in SQL management studio)
BEGIN TRANSACTION
WAITFOR DELAY '00:00:05' --to give me a chance to start the script in multiple tabs
DECLARE #i int
SET #i=0
WHILE #i < 100
BEGIN
DECLARE #Key nvarchar(3)
SET #Key = 'A'+CONVERT(nvarchar(10),#i)
DECLARE #counter bigint
SET #counter=NULL
SELECT #counter=Counter FROM MyCounters WITH (UPDLOCK) WHERE Key = #Key
IF (#counter IS NULL)
BEGIN
-- lock the entire table before inserting (select into variable)
--DECLARE #temp bigint
--SELECT TOP 1 #temp = Counter FROM MyCounters WITH (UPDLOCK, HOLDLOCK)
SET #counter=1
INSERT INTO MyCounters ([Key],Counter) VALUES (#Key, #counter)
END
ELSE
BEGIN
SET #counter=#counter+1
UPDATE MyCounters SET Counter = #counter WHERE Key = #Key
END
SELECT #i, #Key, #counter
SET #i = #i + 1
END;
COMMIT
-- *** Question 1 *** Will a single UPDLOCK be sufficient in the first select to prevent deadlocks?
-- *** Question 2 *** 1. Or do I need to select with UPDLOCK and HOLDLOCK? SELECT #counter=Counter FROM MyCounters WITH (UPDLOCK) WHERE
Key = #Key
In in the default READ COMMITTED isolation level, the UPDLOCK will be released when the statement completes. Specify UPDLOCK,HOLDLOCK to retain locks until the transaction commits or rolls back.
I suggest you change the clustered primary key from the IDENTITY column to the natural key column Key or make the unique constraint the clustered index. This will improve performance. If you don't actually use the Id column, remove it entirely. Throwing an unused IDENTITY surrogate key on tables is a common anti-pattern.
--clustered unique constraint
CREATE TABLE dbo.MyCounters(
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Key] [nvarchar](20) NOT NULL CONSTRAINT UQ_MyCounters_Key UNIQUE CLUSTERED,
[Counter] [bigint] NOT NULL,
CONSTRAINT [PK_Table_1] PRIMARY KEY NONCLUSTERED
(
[Id] ASC
)
);
--remove unneeded surrogate key
CREATE TABLE dbo.MyCounters(
[Key] [nvarchar](20) NOT NULL CONSTRAINT PK_MyCounters PRIMARY KEY CLUSTERED,
[Counter] [bigint] NOT NULL
);
Also, add SET XACT_ABORT ON; to procs with explicit transactions to ensure the transaction is rolled back immediately after errors or client timeouts.

Incorrect syntax when working with a variable in IDENTITY

I'm trying to create a new table with a primary key value that is a continuation of a previous table.
My code is:
DECLARE #Table1_NextKey INT
SELECT #Table1_NextKey = MAX(id) + 1
FROM [Copy of Table1]
CREATE TABLE [dbo].Table1
(
[ID] [int] NOT NULL IDENTITY(#Table1_NextKey, 1)
CONSTRAINT PK_Table1 PRIMARY KEY CLUSTERED,
[PLAN] [nvarchar](255) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
But I get this error:
Msg 102, Level 15, State 1, Line 24
Incorrect syntax near '#Table1_NextKey'
Is there a way to get the Create Table to work with the variable?
You are going about this the wrong way.
What you are clearly trying to do, is copy a table and then you would like to continue the identity values.
In this case, do not declare the seed value differently in the CREATE TABLE, just manually set it afterwards:
CREATE TABLE [dbo].Table1
(
[ID] [int] NOT NULL IDENTITY(1, 1)
CONSTRAINT PK_Table1 PRIMARY KEY CLUSTERED,
[PLAN] [nvarchar](255) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
-- Do some copying code here
DECLARE #Table1_NextKey INT =
(
SELECT #Table1_NextKey = MAX(id) -- not + 1
FROM Table1
);
DBCC CHECKIDENT (Table1 RESEED, #Table1_NextKey) WITH NO_INFOMSGS;
You can only use literal values for identity, you'll need to dynamically construct your create statement, as follows
declare #sql nvarchar(max)=Concat(N'
CREATE TABLE [dbo].Table1(
[ID] [int] NOT NULL IDENTITY(', #Table1_NextKey, N', 1) CONSTRAINT PK_Table1 PRIMARY KEY CLUSTERED,
[PLAN] [nvarchar](255) NULL)
ON [PRIMARY]')
exec sp_executesql #sql
Don't know why SQL Server is so weird about this but with Stu's help, I got this to work:
DECLARE #Table1_NextKey INT,
#SQL_command varchar(4000)
select #Table1_NextKey=max(id)+1 from [Copy of Table1]
set #SQL_command=
'CREATE TABLE [dbo].Table1(
[ID] [int] NOT NULL IDENTITY(' + convert(varchar(5), #Table1_Nextkey) + ', 1) CONSTRAINT PK_Table1 PRIMARY KEY CLUSTERED,
[PLAN] [nvarchar](255) NULL)
ON [PRIMARY]
GO'

There are uncommitted transactions / Lock request time out period exceeded

Please assist. I am trying to write a script to change a data type of a column that has dependants in a table. So i decided to combine scripts and start from creating a temp table to move the data away from the proper table so i can drop it and re create it with a new data type for the column in question. The combined scripts executed fine but now they seem to lock other transaction in the database and when i close the script i get the errors in the title. here is my code below...
SET NOCOUNT ON
BEGIN TRY
BEGIN TRAN
PRINT '1.Drop Temporary Table'
IF EXISTS (SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'EmptyDepot.StoreStockTakeTemp')
AND type in (N'U'))
DROP TABLE EmptyDepot.StoreStockTakeTemp
PRINT '====Table Dropped'
PRINT '2.Create Temporary Table'
CREATE TABLE EmptyDepot.StoreStockTakeTemp
(
StoreStockTakeID INT NOT NULL,
CntrNum VARCHAR(11) NOT NULL,
DepotID INT NOT NULL,
StockCaptureDate DATE NOT NULL,
ChUser VARCHAR(50) NOT NULL,
ChDate SMALLDATETIME NOT NULL,
ChStamp TIMESTAMP NOT NULL,
)
PRINT '====Temporary Table Created'
PRINT '3.Populate Temporary Table'
INSERT INTO EmptyDepot.StoreStockTakeTemp
(
StoreStockTakeID,
CntrNum,
DepotID,
StockCaptureDate,
ChUser,
ChDate
)
SELECT StoreStockTakeID,
CntrNum,
DepotID,
StockCaptureDate,
ChUser,
ChDate
FROM EmptyDepot.StoreStockTake
PRINT '====Temporary Table Populated'
PRINT 'Begin Transaction'
PRINT '================='
PRINT '4.Drop Table'
IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[EmptyDepot].[PK_StoreStockTakeID]') AND parent_object_id = OBJECT_ID(N'[EmptyDepot].[StoreStockTake]'))
ALTER TABLE [EmptyDepot].[StoreStockTake] DROP CONSTRAINT [PK_StoreStockTakeID]
IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[EmptyDepot].[UX_CntrNum_DepotID]') AND parent_object_id = OBJECT_ID(N'[EmptyDepot].[StoreStockTake]'))
ALTER TABLE [EmptyDepot].[StoreStockTake] DROP CONSTRAINT [UX_CntrNum_DepotID]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[EmptyDepot].[StoreStockTake]') AND type in (N'U'))
DROP TABLE [EmptyDepot].[StoreStockTake]
PRINT '====Table Dropped'
PRINT '5.Re-Create Table'
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
SET ANSI_PADDING ON
CREATE TABLE EmptyDepot.StoreStockTake
(
StoreStockTakeID INT NOT NULL,
CntrNum VARCHAR(11) NOT NULL,
DepotID INT NOT NULL,
StockCaptureDate DATE NOT NULL,
ChUser VARCHAR(50) NOT NULL,
ChDate SMALLDATETIME NOT NULL,
ChStamp TIMESTAMP NOT NULL,
CONSTRAINT PK_StoreStockTakeID PRIMARY KEY CLUSTERED
(
StoreStockTakeID ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON "PRIMARY"
) ON "PRIMARY"
PRINT '====Table Created'
PRINT '6.Populate Table'
INSERT
INTO EmptyDepot.StoreStockTake
(
StoreStockTakeID,
CntrNum,
DepotID,
StockCaptureDate,
ChUser,
ChDate
)
SELECT StoreStockTakeID,
CntrNum,
DepotID,
StockCaptureDate,
ChUser,
ChDate
FROM EmptyDepot.StoreStockTakeTemp
PRINT '====Table Populated'
COMMIT TRAN
PRINT 'Transaction Committed'
PRINT '====================='
END TRY
BEGIN CATCH
PRINT ERROR_NUMBER()
PRINT ERROR_MESSAGE()
PRINT ERROR_LINE()
ROLLBACK
PRINT 'Transaction Rolled Back'
PRINT '======================='
END CATCH
Script has some problems..
what happens if someone inserts rows while your script is executing?
is it true you don't need constraint (UX_CntrNum_DepotID) in new table?
what about indexes? there could be none thought..
you probably could just rename temp table so you do not need extra insert..
About uncommited transactions- cant see reason, but maybe you started some transaction before executing the command described?

use sql stored procedure to insert data which is returned from a query stored in a table

sql server 2005
I have a stored procedure which I used to insert data into a table. Some of the data will need to come from the results of executing a query that is stored in a separate table.
The main problem that I keep hitting is not being able to properly execute the returned query. I have tried creating several functions over the past couple of days based on other posts that I have read, but I keep hitting sql errors with exec, execute, sp_executesql, etc.
I am going to paste several scripts that you can use to replicate my environment. I am hoping that someone can please provide an actual code sample which will execute the returned query for use within the stored proc insert function.
Thank you!!!
CREATE TABLE [dbo].[CLIENT](
[cli_id] [int] IDENTITY(1,1) NOT NULL,
[cli_first_name] [varchar](100) NULL,
CONSTRAINT [PK__CLIENT__07F6335A] PRIMARY KEY CLUSTERED
(
[cli_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
INSERT INTO CLIENT (cli_first_name, cli_last_name) values ('tom', 'smith');
go
CREATE TABLE [dbo].[ASSESSMENT_DATALABEL_LIST](
[adl_ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[BoundName] [nvarchar](50) NOT NULL,
[Query] [ntext] NOT NULL,
[KeyFieldName] [nvarchar](50) NOT NULL,
[Status] [nvarchar](20) NOT NULL,
CONSTRAINT [PK_ASSESSMENT_DATALABEL_LIST] PRIMARY KEY CLUSTERED
(
[adl_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
INSERT INTO ASSESSMENT_DATALABEL_LIST (Name, BoundName, Query, KeyFieldName, Status)
values ('Name, First', 'cli_first_name', 'select IsNull(cli_first_name,'''') as cli_first_name FROM CLIENT WHERE cli_id = #KeyFieldValue', 'cli_ID', 'Active')
go
INSERT INTO ASSESSMENT_DATALABEL_LIST (Name, BoundName, Query, KeyFieldName, Status)
values ('Name, Last', 'cli_last_name', 'select IsNull(cli_last_name,'''') as cli_last_name FROM CLIENT WHERE cli_id = #KeyFieldValue', 'cli_ID', 'Active')
go
CREATE TABLE [dbo].[Item_Source]
(
[Item_ID] [int] IDENTITY(1,1) NOT NULL,
[ItemType] [nvarchar](50) NOT NULL,
[ItemCaption] [nvarchar] (50) NULL,
[adl_ID] [int] NOT NULL
)
go
INSERT INTO Item_Source
(ItemType, ItemCaption, adl_ID) values
('DATALABEL', 'First Name',1)
go
INSERT INTO Item_Source
(ItemType, ItemCaption, adl_ID) values
('DATALABEL', 'Last Name',2)
go
CREATE TABLE [dbo].[Item_Destination]
(
[ItemType] [nvarchar](50) NOT NULL,
[ItemCaption] [nvarchar] (50) NULL,
[ItemValue] [nvarchar](50) NULL
)
go
CREATE PROCEDURE [dbo].[spInsertStuff]
#cli_id int
AS
INSERT INTO Item_Destination
(ItemType, ItemCaption, ItemValue)
SELECT
ItemType, ItemCaption, [[[ VALUE OF EXECUTED QUERY FROM ADL TABLE --- dbo.FunctionToGetResultsOfStoredQuery(Item_Source.adl_id, #cli_id) ]]]
FROM Item_Source WHERE Item_Source.Item_ID IN (1,2)
-- this insert will insert both Item_Source rows into Item_Dest with one call. The first row should have an ItemValue of Tom, the second row should have an ItemValue of Smith
GO
You may check this fiddle
The code of the Stored Procedure is:
CREATE PROCEDURE [dbo].[spInsertStuff]
#cli_id int
AS
DECLARE #SQL AS VARCHAR(MAX)
DECLARE #ADL_ID AS INT
DECLARE MyCURSOR
CURSOR FOR
SELECT QUERY, ADL_ID FROM ASSESSMENT_DATALABEL_LIST
OPEN MyCURSOR
FETCH NEXT FROM MyCURSOR INTO #SQL, #ADL_ID
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = REPLACE(#SQL,'#KeyFieldValue',#cli_id)
DECLARE #Temp AS TABLE ([Value] [nvarchar](50))
INSERT INTO #Temp
EXEC (#SQL)
INSERT INTO Item_Destination
(ItemType, ItemCaption, ItemValue)
SELECT
ItemType, ItemCaption, (SELECT [Value] FROM #Temp)
FROM Item_Source
WHERE Item_Source.adl_ID = #ADL_ID
DELETE FROM #Temp
FETCH NEXT FROM MyCURSOR INTO #SQL, #ADL_ID
END
CLOSE MyCURSOR
DEALLOCATE MyCURSOR
GO

Very strange SQL update issue

I have a table called ApprovalTasks... Approvals has a status column
I also have a view called ApprovalsView
When I try a straight update :
update ApprovalTasks set Status = 2 where ApprovalTaskID = 48
I'm getting this error message:
Msg 2601, Level 14, State 1, Line 1
Cannot insert duplicate key row in object 'dbo.ApprovalsView' with unique index 'IX_ApprovalTaskID'.
The statement has been terminated.
Any idea why this is happening?
Here is the create table script:
USE [CSPMOSSApplication]
GO
/****** Object: Table [dbo].[ApprovalTasks] Script Date: 12/11/2008 12:41:35 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[ApprovalTasks](
[ApprovalTaskID] [int] IDENTITY(1,1) NOT NULL,
[ApproverID] [int] NOT NULL,
[DueDate] [datetime] NULL,
[Status] [smallint] NOT NULL,
[ApprovedRejectedDate] [datetime] NULL,
[Reason] [nvarchar](1024) COLLATE Finnish_Swedish_CI_AS NULL,
[OrganizationID] [int] NOT NULL,
[TicketID] [int] NOT NULL,
[Link] [nchar](255) COLLATE Finnish_Swedish_CI_AS NULL,
[GlobalApproverID] [int] NULL,
CONSTRAINT [PK_Approval_Tasks] PRIMARY KEY CLUSTERED
(
[ApprovalTaskID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
USE [CSPMOSSApplication]
GO
ALTER TABLE [dbo].[ApprovalTasks] WITH NOCHECK ADD CONSTRAINT [FK_Approval_Tasks_ApprovalTaskStatuses] FOREIGN KEY([Status])
REFERENCES [dbo].[ApprovalTaskStatuses] ([ApprovalTaskStatusID])
GO
ALTER TABLE [dbo].[ApprovalTasks] CHECK CONSTRAINT [FK_Approval_Tasks_ApprovalTaskStatuses]
GO
ALTER TABLE [dbo].[ApprovalTasks] WITH NOCHECK ADD CONSTRAINT [FK_Approval_Tasks_Organizations] FOREIGN KEY([OrganizationID])
REFERENCES [dbo].[Organizations] ([OrganizationID])
GO
ALTER TABLE [dbo].[ApprovalTasks] CHECK CONSTRAINT [FK_Approval_Tasks_Organizations]
GO
ALTER TABLE [dbo].[ApprovalTasks] WITH NOCHECK ADD CONSTRAINT [FK_Approval_Tasks_Tickets] FOREIGN KEY([TicketID])
REFERENCES [dbo].[Tickets] ([TicketID])
GO
ALTER TABLE [dbo].[ApprovalTasks] CHECK CONSTRAINT [FK_Approval_Tasks_Tickets]
GO
ALTER TABLE [dbo].[ApprovalTasks] WITH NOCHECK ADD CONSTRAINT [FK_Approval_Tasks_Users] FOREIGN KEY([ApproverID])
REFERENCES [dbo].[Users] ([UserID])
GO
ALTER TABLE [dbo].[ApprovalTasks] CHECK CONSTRAINT [FK_Approval_Tasks_Users]
PK_Approval_Tasks(Clustered)
USE [CSPMOSSApplication]
GO
/****** Object: Index [PK_Approval_Tasks] Script Date: 12/11/2008 12:45:50 ******/
ALTER TABLE [dbo].[ApprovalTasks] ADD CONSTRAINT [PK_Approval_Tasks] PRIMARY KEY CLUSTERED
(
[ApprovalTaskID] ASC
)WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]
IX_ApprovalTaskID(Clsutered)
SE [CSPMOSSApplication]
GO
SET ARITHABORT ON
GO
SET CONCAT_NULL_YIELDS_NULL ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
SET ANSI_PADDING ON
GO
SET ANSI_WARNINGS ON
GO
SET NUMERIC_ROUNDABORT OFF
GO
/****** Object: Index [IX_ApprovalTaskID] Script Date: 12/11/2008 12:47:27 ******/
CREATE UNIQUE CLUSTERED INDEX [IX_ApprovalTaskID] ON [dbo].[ApprovalsView]
(
[ApprovalTaskID] ASC
)WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]
Create View Script
USE [CSPMOSSApplication]
GO
-- =============================================
-- Script Template
-- =============================================
-- [ApprovalTasks]: add columns Link, GlobalApproverID
IF NOT EXISTS(SELECT 1 FROM sysobjects,syscolumns WHERE sysobjects.id = syscolumns.id
AND sysobjects.name = 'ApprovalTasks' AND syscolumns.name = 'Link')
BEGIN
ALTER TABLE ApprovalTasks ADD [Link] [nchar] (255) COLLATE Finnish_Swedish_CI_AS NULL
PRINT 'Column ApprovalTasks.Link was added.'
END
IF NOT EXISTS(SELECT 1 FROM sysobjects,syscolumns WHERE sysobjects.id = syscolumns.id
AND sysobjects.name = 'ApprovalTasks' AND syscolumns.name = 'GlobalApproverID')
BEGIN
ALTER TABLE ApprovalTasks ADD [GlobalApproverID] [int] NULL
PRINT 'Column ApprovalTasks.GlobalApproverID was added.'
ALTER TABLE [dbo].[ApprovalTasks] WITH NOCHECK ADD CONSTRAINT [FK_Approval_Tasks_GlobalApproverID] FOREIGN KEY([GlobalApproverID])
REFERENCES [dbo].[Users] ([UserID])
ALTER TABLE [dbo].[ApprovalTasks] CHECK CONSTRAINT [FK_Approval_Tasks_GlobalApproverID]
END
-- [ApprovalsView]
IF EXISTS (SELECT * FROM sys.fulltext_indexes fti WHERE fti.object_id = OBJECT_ID(N'[dbo].[ApprovalsView]'))
BEGIN
DROP FULLTEXT INDEX ON [dbo].[ApprovalsView]
PRINT 'FULLTEXT INDEX on [ApprovalsView] was dropped.'
END
GO
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[ApprovalsView]') AND name = N'IX_ApprovalTaskID')
BEGIN
DROP INDEX IX_ApprovalTaskID ON [dbo].[ApprovalsView] WITH ( ONLINE = OFF )
PRINT 'INDEX IX_ApprovalTaskID was dropped.'
END
GO
IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[ApprovalsView]'))
DROP VIEW [dbo].[ApprovalsView]
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE VIEW [dbo].[ApprovalsView]
WITH SCHEMABINDING
AS
SELECT at.ApprovalTaskID,
at.ApproverID,
at.DueDate,
at.Status,
ats.ApprovalTaskStatusTranslationKey AS StatusText,
at.ApprovedRejectedDate,
at.Reason,
at.OrganizationID,
ord.Name AS OrderName,
ord.TotalPrice,
ord.SubmitDate,
ord.OrdererID,
usr.FirstName AS OrdererFirstName,
usr.LastName AS OrdererLastName,
ordi.Items_Name AS ItemName,
ordi.Items_Description AS ItemDescription,
ordi.OtherInformation AS ItemInformation,
oir.RecipientFullName,
CONVERT(nvarchar(250), oir.DeliveryAddress) As DeliveryAddress,
ti.Description
FROM dbo.ApprovalTasks at
INNER JOIN
dbo.ApprovalTaskStatuses ats ON ats.ApprovalTaskStatusID = at.Status
INNER JOIN
dbo.Orders_Items_Recipients oir ON oir.TicketID = at.TicketID
INNER JOIN
dbo.Orders_Items ordi ON ordi.Orders_ItemsID = oir.Orders_ItemsID
INNER JOIN
dbo.Orders ord ON ordi.OrderID = ord.OrderID
INNER JOIN
dbo.Users usr ON ord.OrdererID = usr.UserID
INNER JOIN
dbo.Tickets ti ON ti.TicketID = at.TicketID
GO
CREATE UNIQUE CLUSTERED INDEX [IX_ApprovalTaskID] ON [dbo].[ApprovalsView]
(
[ApprovalTaskID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
GO
CREATE FULLTEXT INDEX ON [dbo].[ApprovalsView](
[DeliveryAddress] LANGUAGE [Neutral],
[ItemDescription] LANGUAGE [Neutral],
[ItemInformation] LANGUAGE [Neutral],
[ItemName] LANGUAGE [Neutral],
[OrdererFirstName] LANGUAGE [Neutral],
[OrdererLastName] LANGUAGE [Neutral],
[OrderName] LANGUAGE [Neutral],
[Reason] LANGUAGE [Neutral],
[RecipientFullName] LANGUAGE [Neutral])
KEY INDEX [IX_ApprovalTaskID] ON [ApprovalSearchCatalog]
WITH CHANGE_TRACKING AUTO
GO
ALTER FULLTEXT CATALOG [ApprovalSearchCatalog] rebuild
PRINT 'Catalog [ApprovalSearchCatalog] task to rebuild fulltext index was sent.'
-- STORED PROCEDURES
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ReceiveApprovalTasksFromQueue]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[ReceiveApprovalTasksFromQueue]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
EXEC dbo.sp_executesql #statement = N'
-- =============================================
-- Author: Petr Klozik
-- Create date: 19.11.2008
-- Description: Gets approvals which DueDate is over ReferenceDate (now)
-- =============================================
CREATE Procedure [dbo].[ReceiveApprovalTasksFromQueue]
#Limit int
As
BEGIN
SET NOCOUNT ON;
If Not #Limit Is Null Set RowCount #Limit
-- Status: WaitingForApproval = 1
Select Tasks.ApprovalTaskID
From ApprovalTasks Tasks
Where Status = 1 And DueDate < GetDate()
END
'
GO
GRANT EXECUTE ON [dbo].[ReceiveApprovalTasksFromQueue] TO [OMT_IntegrationRole]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[UpdateApprovalTaskInfo]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[UpdateApprovalTaskInfo]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
EXEC dbo.sp_executesql #statement = N'
-- =============================================
-- Author: Klozik Petr
-- Create date: 2008-11-25
-- Description: Updates Approval task info to DB
-- =============================================
CREATE PROCEDURE [dbo].[UpdateApprovalTaskInfo]
#ApprovalTaskID int,
#DueDate datetime,
#ApprovalRejectDate datetime,
#Reason nvarchar(1024),
#Status int,
#GlobalApproverID int
AS
BEGIN
SET NOCOUNT ON;
Update ApprovalTasks
Set DueDate = #DueDate,
ApprovedRejectedDate = #ApprovalRejectDate,
Reason = #Reason,
Status = #Status,
GlobalApproverID = #GlobalApproverID
Where ApprovalTaskID = #ApprovalTaskID
END
'
GO
GRANT EXECUTE ON [dbo].[UpdateApprovalTaskInfo] TO [OMT_IntegrationRole]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[GetUserById]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[GetUserById]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
EXEC dbo.sp_executesql #statement = N'
-- =============================================
-- Author: Klozik Petr
-- Create date: 2008-12-04
-- Description: Gets user row by the specified ID.
-- =============================================
CREATE PROCEDURE [dbo].[GetUserById]
(
#UserID int
)
AS
BEGIN
SELECT
UserID,
RTRIM(SID) [SID],
RTRIM(OMTGUID) [OMTGUID],
RTRIM(UserAccount) [UserAccount],
RTRIM(Email) [Email],
RTRIM(FirstName) [FirstName],
RTRIM(LastName) [LastName],
RTRIM(Country) [Country],
RTRIM(City) [City],
RTRIM(PostalNumber) [PostalNumber],
RTRIM(StreetAddress) [StreetAddress],
RTRIM(PhoneNumber) PhoneNumber,
Modified,
Deleted,
Uploaded,
UploadCode,
UploadStatus,
RTRIM(Users.ADUserAccount) AS ADUserAccount
FROM
[dbo].[Users]
WHERE
UserID = #UserID
END
'
GO
GRANT EXECUTE ON [dbo].[GetUserById] TO [OMT_IntegrationRole]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[GetApprovalTaskInfoById]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[GetApprovalTaskInfoById]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
EXEC dbo.sp_executesql #statement = N'
-- =============================================
-- Author: Petr Klozik
-- Create date: 19.11.2008
-- Description: Gets approvals which DueDate is over ReferenceDate (now)
-- =============================================
CREATE Procedure [dbo].[GetApprovalTaskInfoById]
#ApprovalTaskID int
As
BEGIN
SET NOCOUNT ON;
Declare #OrganizationID int
Declare #CurrentApproverID int
Declare #NewApproverID int
Declare #NewOrganizationID int
Select #OrganizationID = OrganizationID, #CurrentApproverID = ApproverID
From ApprovalTasks
Where ApprovalTaskID = #ApprovalTaskID
Set #NewApproverID = (
Select Top 1 o.GlobalApproverID
From Organizations o
Inner Join OrganizationDescendants od On od.OrganizationID = o.OrganizationID
Where od.DescendantID = #OrganizationID
And Not(o.GlobalApproverID Is Null)
Order By o.OrganizationLevel Desc
)
If Not(#NewApproverID Is Null)
Begin
Set #NewOrganizationID = (
Select OrganizationID
from Organizations
Where GlobalApproverID = #NewApproverID)
End
Select Tasks.*, Tickets.Description AS TicketDescription,
Tickets.RequestorID, Tickets.OrdererID,
#NewApproverID AS OrgGlobalApproverID,
#NewOrganizationID AS OrgGlobalApproverOrganizationID
From ApprovalTasks Tasks
inner join Tickets Tickets on Tasks.TicketID = Tickets.TicketID
Where ApprovalTaskID = #ApprovalTaskID
END
'
GO
GRANT EXECUTE ON [dbo].[GetApprovalTaskInfoById] TO [OMT_IntegrationRole]
GO
Lucky guess: any update triggers defined?
2nd lucky guess: ApprovalsView is an indexed view whose index is violated after updating the ApprovalTask table.
look at the definition of the index IX_ApprovalTaskID
Is it possible there is a unique key constraint on ApprovalTaskID, StatusID which would mean there is another row in the table with Status = 2 & ApprovalTaskID = 48
I agree with user Learning, it looks like there's a FOR UPDATE trigger on ApprovalTasks that is inserting an ApprovalTaskID into ApprovalView
Try running DISABLE TRIGGER ALL ON ApprovalTasks and re-run the update
Since the error is coming from the object dbo.ApprovalsView , the problem is with a trigger on ApprovalsTask which is trying to update that table. I really think that ApprovalsView is a table and not a view. But you must have already checked for that.
It looks like a view has been created which is either of, or includes, the table you are updating and that the error occurs when the view is being updated with the changes.
Perhaps the view contains data from different tables which is incompatible, or has stricter constraints set?