触发动态更新排名

时间:2013-12-26 05:27:50

标签: sql-server-2008-r2

我想为insert update和Delete编写触发器。 O有一个名为(tbl_rank)的表,它具有主键(ID)。

ID   Name   Rank
1    A      1
2    B      2
3    C      3
4    D      4
5    E      5

现在我要插入新的排名,但条件是

1) if I enter 6 it will be 6
2) if I enter 7 it also should be 6 (I mean in sequence)
3) if I enter 2 than than entered rank will be 2 and 2 will be 3 and so on

删除触发器

1) if I delete 5 the rank should be 1 to 4
2) if I delete 2 the rank would be rearranged and 3 should be 2 and 4 would be 3 and so on

用于更新触发器

1) if I update 3 to 5 than 4 would be 3 and 5 would be 4 
2) if I update 5 to 3 than 3 would be 4 and 4 would be 5

我写了插入和删除触发它的工作正常,但在更新中我得到不均匀的结果。

3 个答案:

答案 0 :(得分:0)

你能不能将tbl_rank视为一个视图,那么你不需要任何触发器吗?要在视图中对它们进行排名,您可以使用窗口函数row_number()(按Id排序)

初始更新是如何进行的?如果您知道它是更新,那么您需要执行删除并仅插入受影响的范围。例如,将3更改为5.删除3到5的记录,然后使用不同的ID再次插入这3条记录。无论如何,更新语句基本上都是这样做

答案 1 :(得分:0)

假设id不是自动标识列。

CREATE TRIGGER trg_tbl_rank ON tbl_rank INSTEAD OF INSERT,DELETE,UPDATE AS BEGIN SET NOCOUNT ON;

DECLARE @v_deleted_rank INT;
DECLARE @v_inserted_rank INT;
DECLARE @v_max_rank INT;

SELECT @v_deleted_rank = COALESCE(rank, 0) FROM deleted;
SELECT @v_inserted_rank = COALESCE(rank, 0) FROM inserted;
SELECT @v_max_rank = COALESCE(MAX(rank), 0) FROM tbl_rank;

IF @v_deleted_rank > 0
BEGIN
    DELETE FROM tbl_rank
        WHERE id = (SELECT id FROM deleted);
    UPDATE tbl_rank
        SET rank = rank - 1
        WHERE rank > @v_deleted_rank;
END
IF @v_inserted_rank > 0
BEGIN
    IF @v_inserted_rank <= @v_max_rank
    BEGIN
        UPDATE tbl_rank
            SET rank = rank + 1
            WHERE rank >= @v_inserted_rank;
        INSERT INTO tbl_rank (id, name, rank)
            SELECT id, name, @v_inserted_rank FROM inserted;
    END
    ELSE
        INSERT INTO tbl_rank (id, name, rank)
            SELECT id, name, @v_max_rank + 1 FROM inserted;
END

DECLARE @v_deleted_rank INT; DECLARE @v_inserted_rank INT; DECLARE @v_max_rank INT; SELECT @v_deleted_rank = COALESCE(rank, 0) FROM deleted; SELECT @v_inserted_rank = COALESCE(rank, 0) FROM inserted; SELECT @v_max_rank = COALESCE(MAX(rank), 0) FROM tbl_rank; IF @v_deleted_rank > 0 BEGIN DELETE FROM tbl_rank WHERE id = (SELECT id FROM deleted); UPDATE tbl_rank SET rank = rank - 1 WHERE rank > @v_deleted_rank; END IF @v_inserted_rank > 0 BEGIN IF @v_inserted_rank <= @v_max_rank BEGIN UPDATE tbl_rank SET rank = rank + 1 WHERE rank >= @v_inserted_rank; INSERT INTO tbl_rank (id, name, rank) SELECT id, name, @v_inserted_rank FROM inserted; END ELSE INSERT INTO tbl_rank (id, name, rank) SELECT id, name, @v_max_rank + 1 FROM inserted; END

以下是要测试的查询:

END
GO

答案 2 :(得分:0)

  

我想为插入更新编写触发器并删除我有一个名为(tbl_rank)的表,其中包含主键(ID)

请发布DDL,以便人们不必猜测架构中的密钥,约束,声明性参照完整性,数据类型等。了解如何遵循ISO-11179数据元素命名约定和格式规则。时态数据应使用ISO-8601格式。代码应该尽可能在标准SQL中,而不是本地方言。

这是SQL论坛上的最小行为。将“tbl_”放在表名上是一个经典的设计缺陷,称为“tbling”,列名也违反了ISO-11179规则。现在我们必须猜测键,数据类型等。这是我的猜测和清理。

CREATE TABLE Prizes
(prize_id INTEGER NOT NULL PRIMARY KEY, 
 prize_name CHAR(1) NOT NULL, 
 prize_rank INTEGER NOT NULL);

INSERT INTO Prizes
VALUES
(1, 'A', 1), 
(2, 'B', 2), 
(3, 'C', 3), 
(4, 'D', 4), 
(5, 'E', 5);

为何触发? RDBMS具有虚拟表和列。这不是一副穿孔卡或磁带文件。 VIEW始终是最新且正确的。

CREATE VIEW Prize_List
AS
SELECT prize_id, prize_name,
       ROW_NUMBER() OVER (ORDER BY prize_id)
       AS prize_rank
  FROM Prizes;

但最好完全删除prize_id列并根据prize_rank列重新排列显示顺序:

CREATE TABLE Prizes
(prize_name CHAR(1) NOT NULL, 
 prize_rank INTEGER NOT NULL PRIMARY KEY);

现在使用过程根据需要操作表。

CREATE PROCEDURE Swap_Prize_Ranks (@old_prize_rank INTEGER, @new_prize_rank INTEGER)
AS 
UPDATE Prizes 
   SET prize_rank
       = CASE prize_rank 
         WHEN @old_prize_rank 
         THEN @new_prize_rank 
         ELSE prize_rank + SIGN(@old_prize_rank - @new_prize_rank) 
         END 
 WHERE prize_rank BETWEEN @old_prize_rank AND @new_prize_rank 
    OR prize_rank BETWEEN @new_prize_rank AND @old_prize_rank;

如果要删除几行,请记住用以下方法填补空白:

CREATE PROCEDURE Close_Prize_Gaps()
AS 
UPDATE Prizes 
   SET prize_rank
       = (SELECT COUNT (P1.prize_rank)
            FROM Prizes AS P1
           WHERE P1.prize_rank <= Prizes.prize_rank);