I need to maintain a column that stores a sort order partitioned by a parent record foreign key. The column should be the contiguous counting numbers and should automatically renumber the records when the sort order is changed for one record.
Here's what I have so far:
CREATE TABLE SalesOrder (
SalesOrderKey INT IDENTITY(1,1) NOT NULL
, CONSTRAINT PK_SalesOrder PRIMARY KEY (SalesOrderKey)
, OrderName VARCHAR(50)
);
CREATE TABLE SalesOrderLine (
LineKey INT IDENTITY(1,1) NOT NULL
, CONSTRAINT PK_Line PRIMARY KEY (LineKey)
, SalesOrderKey INT NOT NULL
, CONSTRAINT FK_SalesOrderLine_SalesOrder FOREIGN KEY (SalesOrderKey) REFERENCES SalesOrder(SalesOrderKey)
, LineNumber INT
, LineNotes VARCHAR(50)
);
GO
CREATE TRIGGER NewOrderLine ON SalesOrderLine
AFTER INSERT
AS
BEGIN
IF (ROWCOUNT_BIG() <> 1)
RETURN;
DECLARE @Line INT
, @LineKey INT
, @Key INT;
SELECT @Key = SalesOrderKey
, @LineKey = LineKey
, @Line = LineNumber
FROM inserted;
-- If line number inserted, bump every line after that number
UPDATE SalesOrderLine
SET LineNumber += 1
WHERE LineNumber >= @Line
AND SalesOrderKey = @Key
AND LineKey <> @LineKey;
-- If line number inserted, set it to the last line
UPDATE SalesOrderLine
SET LineNumber = (
SELECT MAX(LineNumber) + 1
FROM SalesOrderLine
WHERE SalesOrderKey = @Key)
WHERE LineKey = @LineKey
AND LineNumber IS NULL;
-- Verify numbers are contiguous
WITH num AS (
SELECT LineKey id,
ROW_NUMBER() OVER (ORDER BY LineNumber) ln
FROM SalesOrderLine
WHERE SalesOrderKey = @Key
)
UPDATE SalesOrderLine
SET LineNumber = ln
FROM num
WHERE LineKey = num.id;
END;
GO
CREATE TRIGGER UpdateLineNumber ON SalesOrderLine
AFTER UPDATE, DELETE
AS
BEGIN
IF (ROWCOUNT_BIG() <> 1)
RETURN;
DECLARE @Line INT
, @Key INT
, @LineKey INT
, @OldLine INT;
SELECT @Line = LineNumber
FROM inserted;
SELECT @Key = SalesOrderKey
, @LineKey = LineKey
, @OldLine = LineNumber
FROM deleted;
-- Record moved up in list
IF @OldLine > @Line
UPDATE SalesOrderLine
SET LineNumber += 1
WHERE LineNumber >= @Line
AND SalesOrderKey = @Key
AND LineKey != @LineKey;
ELSE -- Record moved down in list or deleted
UPDATE SalesOrderLine
SET LineNumber -= 1
WHERE LineNumber BETWEEN @OldLine AND @Line
AND SalesOrderKey = @Key
AND LineKey != @LineKey;
-- Verify numbers are contiguous
WITH num AS (
SELECT LineKey id,
ROW_NUMBER() OVER (ORDER BY LineNumber) ln
FROM SalesOrderLine
WHERE SalesOrderKey = @Key
)
UPDATE SalesOrderLine
SET LineNumber = ln
FROM num
WHERE LineKey = num.id;
END;
This works, but doesn't seem like the best way to accomplish this. Is it feasible to use only one trigger containing only one statement? Is there a better way to do this?