【问题标题】:TSQL: Create A Custom Identity Column (Revision Database)TSQL:创建自定义标识列(修订数据库)
【发布时间】:2011-06-08 04:06:09
【问题描述】:

如何创建类似于身份自定义触发器计算列

我有两个字段需要按特定顺序自动递增:IDREV

图纸 身份证 |转 |信息 ------+--------+------ 1 | 0 | “画1” 2 | 0 | “画2” 2 | 1 | “Draw2Edit” 2 | 2 | “Draw2MoreEdit” 3 | 0 | 《画3》 4 | 0 | “画4”

如果我要在我的表中插入更多记录,例如:

INSERT INTO DRAWINGS (INFO) VALUES ("Draw5")
INSERT INTO DRAWINGS (ID,INFO) VALUES (3,"Draw3Edit")

我的桌子想要:

图纸 身份证 |转 |信息 ------+--------+------ 1 | 0 | “画1” 2 | 0 | “画2” 2 | 1 | “Draw2Edit” 2 | 2 | “Draw2MoreEdit” 3 | 0 | 《画3》 3 | 1 | “Draw3Edit”——新行 4 | 0 | “画4” 5 | 0 | “Draw5”——新行

伪代码

-- IF ID==NULL AND REV==NULL THEN IT IS A NEW DRAWING
IF INSERTED.ID IS NULL AND INSERTED.REV IS NULL 
   ID = SELECT MAX(ID)+1 FROM DRAWINGS
   REV = 0
   --INSERT HERE STATEMENT HERE
ELSE
   -- IF ID!=NULL AND REV==NULL THEN IT IS A NEW REVISION
   IF INSERTED.ID IS NOT NULL AND INSERTED.REV IS NULL
       -- EXTRA CHECK TO ENSURE DRAWING ACTUALLY EXISTS
       IF EXISTS(INSERTED.ID)
           REV = SELECT MAX(REV) + 1 FROM DRAWINGS WHERE ID = INSERTED.ID
           --INSERT HERE STATEMENT HERE
       ELSE
           --DRAWING DOES NOT ACTUALLY EXIST
           GOTO ERROR
   ELSE
       --REV IS NOT NULL (REVISIONS ARE A COMPUTED VALUE ERROR)
       GOTO ERROR

注意:

  • 我正在使用 SQL Server 2000
  • 该表仅用于插入。

我想我已经接近了我想要的:

DROP TABLE DRAW

GO

CREATE TABLE DRAW
(
    ID INT DEFAULT(0), 
    REV INT DEFAULT(-1), 
    INFO VARCHAR(10), 
    PRIMARY KEY(ID, REV)
)

GO

CREATE TRIGGER TRIG_DRAW ON DRAW
FOR INSERT
AS
BEGIN
    DECLARE @newId INT,
            @newRev INT,
            @insId INT,
            @insRev INT

    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    BEGIN TRANSACTION

    SELECT @insId = ID FROM inserted
    SELECT @insRev = REV FROM inserted

    PRINT 'BEGIN TRIG'
    PRINT @insId
    PRINT @insRev
    PRINT @newId
    PRINT @newRev


    --IF ID=0 THEN IT IS A NEW ID
    IF @insId <=0
    BEGIN
        --NEW DRAWING ID=MAX+1 AND REV=0
        SELECT @newId = COALESCE(MAX(ID), 0) + 1 FROM DRAW
        SELECT @newRev = 0
    END
    ELSE
    --ELSE IT IS A NEW REV
    BEGIN
        --CHECK TO ENSURE ID EXISTS
        IF EXISTS(SELECT * FROM DRAW WHERE ID=@insId AND REV=0)
        BEGIN
            PRINT 'EXISTS'
            SELECT @newId = @insId
            SELECT @newRev = MAX(REV) + 1 FROM DRAW WHERE ID=@insID
                                                            AND   REV=-1
        END
        ELSE
        --ID DOES NOT EXIST THEREFORE NO REVISION
        BEGIN
            RAISERROR 50000 'ID DOES NOT EXIST.'
            ROLLBACK TRANSACTION
            GOTO END_TRIG
        END
    END

    PRINT 'END TRIG'
    PRINT @insId
    PRINT @insRev
    PRINT @newId
    PRINT @newRev

    SELECT * FROM DRAW

    UPDATE DRAW SET ID=@newId, REV=@newRev WHERE ID=@insId



    COMMIT TRANSACTION
    END_TRIG:
END

GO


INSERT INTO DRAW (INFO) VALUES ('DRAW1')
INSERT INTO DRAW (INFO) VALUES ('DRAW2')
INSERT INTO DRAW (ID,INFO) VALUES (2,'DRAW2EDIT1') --PROBLEM HERE
INSERT INTO DRAW (ID,INFO) VALUES (2,'DRAW2EDIT2')
INSERT INTO DRAW (INFO) VALUES ('DRAW3')
INSERT INTO DRAW (INFO) VALUES ('DRAW4')

GO

--SHOULD THROW
INSERT INTO DRAW (ID,INFO) VALUES (9,'DRAW9')

GO

SELECT * FROM DRAW

GO

【问题讨论】:

  • 你删除过行吗?如果是这样,那会发生什么?如果删除组中的最后一个,新插入的行是否会被赋予相同的值?值是顺序的重要吗?如果是这样,这将影响并发性,因为在事务回滚时需要阻止并发插入。
  • @Martin:这是一个很好的观点。该数据库旨在仅允许插入。顺序插入最好,但这不是必需的,所以 REV 0、1、2、6、7、8(其中 3..5 插入失败并被忽略)。
  • 这实际上是相当困难的。试图想出一种解决方案,避免 2 个并发事务被分配相同的数字而不会阻塞。 are a couple of approaches here 他们需要调整才能与任意数量的子组一起工作。
  • 使用 'INSTEAD OF INSERT' 的触发器如何像 BEFORE INSERT 一样工作?一个问题是它不允许 NULL。我可以使用 0 作为默认值并从 1 开始我的修订和绘图。我可以修改 inserted吗?如:插入的.REV = 1; INSERT INTO DRAWING(选择 * FROM 已插入);
  • @snmcdonald - 我已经发布的链接中的第二个版本的效率似乎略低!

标签: sql database tsql triggers sql-server-2000


【解决方案1】:

使用时间戳将插入按正确的顺序排列,然后在选择行时在 SP 或客户端程序中按程序分配序数值。

【讨论】:

  • 使用row_numberpartition 更容易。这假设 OP 也总是带回整个表(假设 IDREV 具有特定的常量值很重要)
【解决方案2】:
DROP TABLE DRAW

GO

CREATE TABLE DRAW
(
    ID INT DEFAULT(0), 
    REV INT DEFAULT(-1), 
    INFO VARCHAR(10), 
    PRIMARY KEY(ID, REV)
)

GO

CREATE TRIGGER TRIG_DRAW ON DRAW
FOR INSERT
AS
BEGIN
    DECLARE @newId INT,
            @newRev INT,
            @insId INT,
            @insRev INT

    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    BEGIN TRANSACTION

    SELECT @insId = ID FROM inserted
    SELECT @insRev = REV FROM inserted

    --IF ID=0 THEN IT IS A NEW ID
    IF @insId <=0
    BEGIN
        --NEW DRAWING ID=MAX+1 AND REV=0
        SELECT @newId = COALESCE(MAX(ID), 0) + 1 FROM DRAW
        SELECT @newRev = 0
    END
    ELSE
    --ELSE IT IS A NEW REV
    BEGIN
        --CHECK TO ENSURE ID EXISTS (NOT COUNTING JUST INSERTED)
        IF (SELECT COUNT(*) FROM DRAW WHERE ID=@insId) > 1
        BEGIN
            SELECT @newId = @insId
            SELECT @newRev = MAX(REV) + 1 FROM DRAW WHERE ID=@insID
        END
        ELSE
        --ID DOES NOT EXIST THEREFORE NO REVISION
        BEGIN
            RAISERROR 50000 'ID DOES NOT EXIST.'
            ROLLBACK TRANSACTION
            GOTO END_TRIG
        END
    END

    UPDATE DRAW SET ID=@newId, REV=@newRev WHERE ID=@insId AND REV=-1

    COMMIT TRANSACTION
    END_TRIG:
END

GO


INSERT INTO DRAW (INFO) VALUES ('DRAW1')
INSERT INTO DRAW (INFO) VALUES ('DRAW2')
INSERT INTO DRAW (ID,INFO) VALUES (2,'DRAW2EDIT1') --PROBLEM HERE
INSERT INTO DRAW (ID,INFO) VALUES (2,'DRAW2EDIT2')
INSERT INTO DRAW (INFO) VALUES ('DRAW3')
INSERT INTO DRAW (INFO) VALUES ('DRAW4')

GO

--SHOULD THROW
INSERT INTO DRAW (ID,INFO) VALUES (9,'DRAW9')

GO

SELECT * FROM DRAW

GO

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-22
    • 2012-01-04
    • 2014-10-04
    • 2016-02-20
    • 1970-01-01
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多