I have a table with an “order” column which needs to maintain a contiguous range of unique numbers. What I’m trying to create is a trigger that fires after deleting rows which updates the “order” column so that the numbers remain contiguous.
I know a lot of people would argue that an “order” column only needs to be continuous, not contiguous, however there is a lot of front end JavaScript, and other SQL, for ordering/reordering these items which depends on order being contiguous. I would prefer to simply get this trigger working rather than having to rewrite that, of course I’m open to suggestions 😉
The trigger I have works fine for a single row delete, but when a multiple row delete occurs, only the first row gets deleted and the rest remain with no error thrown.
I thought the problem may have been recursion, as it updates the table it fired from, but it’s only a delete trigger so I don’t think that’s the problem. Turning off RECURSIVE_TRIGGERS didn’t fix the issue.
Here’s the code:
CREATE TABLE [dbo].[Item]
(
[ItemID] INT NOT NULL IDENTITY(1, 1),
[ItemOrder] INT NOT NULL,
[ItemName] NVARCHAR (50) NOT NULL
)
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
CREATE TRIGGER [dbo].[trItem_odr] ON [dbo].[Item]
AFTER DELETE
AS
BEGIN
SET NOCOUNT ON ;
DECLARE @MinOrder INT
SELECT @MinOrder = MIN(ItemOrder)
FROM DELETED
DECLARE @UpdatedItems TABLE
(
ID INT IDENTITY(0, 1)
PRIMARY KEY,
ItemID INT
)
INSERT INTO @UpdatedItems (ItemID)
SELECT ItemID
FROM dbo.Item
WHERE ItemOrder > @MinOrder
AND ItemID NOT IN (SELECT ItemID
FROM DELETED)
ORDER BY ItemOrder
UPDATE dbo.Item
SET ItemOrder = (SELECT ID + @MinOrder
FROM @UpdatedItems
WHERE ItemID = Item.ItemID)
WHERE ItemID IN (SELECT ItemID
FROM @UpdatedItems)
END
GO
ALTER TABLE [dbo].[Item] ADD CONSTRAINT [PK_Item] PRIMARY KEY CLUSTERED ([ItemID])
GO
ALTER TABLE [dbo].[Item] ADD CONSTRAINT [IX_Item_1] UNIQUE NONCLUSTERED ([ItemName])
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_Item_2] ON [dbo].[Item] ([ItemOrder])
GO
INSERT INTO [dbo].[Item] ([ItemOrder], [ItemName])
SELECT 1, N'King Size Bed' UNION ALL
SELECT 2, N'Queen size bed' UNION ALL
SELECT 3, N'Double Bed' UNION ALL
SELECT 4, N'Single Bed' UNION ALL
SELECT 5, N'Filing Cabinet' UNION ALL
SELECT 6, N'Washing Machine' UNION ALL
SELECT 7, N'2 Seater Couch' UNION ALL
SELECT 8, N'3 Seater Couch' UNION ALL
SELECT 9, N'1 Seater Couch' UNION ALL
SELECT 10, N'Flat Screen TV' UNION ALL
SELECT 11, N'Fridge' UNION ALL
SELECT 12, N'Dishwasher' UNION ALL
SELECT 13, N'4 Seater couch' UNION ALL
SELECT 14, N'Lawn Mower' UNION ALL
SELECT 15, N'Dining table'
GO
Rewrite your front end. Prefer to trade more development time for less runtime.
Keeping the order column contiguous by updating all out-of-order rows in the table is tremendously inefficient (remove item 1 => update 100000000 items) resulting in potentially huge update operations, is going to generate tremendous contention because updates modify a lot of rows so they content with almost any read, and ultimately, incorrect under concurrency (you’ll end up with gaps and overlaps anyway). Don’t do it.