使用序列使用连续值填充多个列

时间:2015-10-13 14:49:44

标签: tsql sql-server-2012 sequence

如何使用默认约束,触发器或其他一些机制自动将序列中的多个连续值插入到表格同一行的多个列中?

SQL Server中序列的标准用法是将其与多个表上的默认约束相结合,从而基本上获得跨表标识。例如,请参阅Microsoft文档文章“Sequence Numbers”中的“C.在多个表中使用序列号”部分。

如果您只想从插入的每一行的序列中获取单个值,这非常有用。但有时候我希望得到多个连续的值。所以理论上我会创建一个这样的序列和表:

CREATE SEQUENCE DocumentationIDs;

CREATE TABLE Product
    (
      ProductID BIGINT NOT NULL IDENTITY(1, 1) PRIMARY KEY
    , ProductName NVARCHAR(100) NOT NULL
    , MarketingDocumentationID BIGINT NOT NULL DEFAULT ( NEXT VALUE FOR DocumentationIDs )
    , TechnicalDocumentationID BIGINT NOT NULL DEFAULT ( NEXT VALUE FOR DocumentationIDs )
    , InternalDocumentationID BIGINT NOT NULL DEFAULT ( NEXT VALUE FOR DocumentationIDs )
    );

不幸的是,这会在所有三列中插入相同值。这是by design

  

如果NEXT VALUE FOR函数的多个实例在单个Transact-SQL语句中指定相同的序列生成器,则所有这些实例将为该Transact-SQL语句处理的给定行返回相同的值。此行为与ANSI标准一致。

黑客增量

我在网上找到的唯一suggestion就是使用一个hack,你需要插入的列数增加(在我设计的例子中有三个)并手动添加到{{1} }默认约束中的函数:

NEXT VALUE FOR

对我有用,因为并非所有使用我的序列的表都需要相同数量的值。

1 个答案:

答案 0 :(得分:1)

使用AFTER INSERT触发器的一种可能方法是:

表定义需要改变slighlty(DocumentationID列应默认为0,或允许可以为空):

CREATE TABLE Product
    (
      ProductID BIGINT NOT NULL IDENTITY(1, 1)
    , ProductName NVARCHAR(100) NOT NULL
    , MarketingDocumentationID BIGINT NOT NULL CONSTRAINT DF_Product_1 DEFAULT (0)
    , TechnicalDocumentationID BIGINT NOT NULL CONSTRAINT DF_Product_2 DEFAULT (0)
    , InternalDocumentationID BIGINT NOT NULL CONSTRAINT DF_Product_3 DEFAULT (0)
    , CONSTRAINT PK_Product PRIMARY KEY (ProductID)
    );

完成工作的触发器如下:

CREATE TRIGGER Product_AfterInsert ON Product
AFTER INSERT
AS
BEGIN
    SET NOCOUNT ON;

    IF NOT EXISTS (SELECT 1 FROM INSERTED)
        RETURN;

    CREATE TABLE #DocIDs
        (
        ProductID BIGINT NOT NULL
        , Num INT NOT NULL
        , DocID BIGINT NOT NULL
        , PRIMARY KEY (ProductID, Num)
        );

    INSERT INTO #DocIDs (ProductID, Num, DocID)
        SELECT
            i.ProductID
            , r.n
            , NEXT VALUE FOR DocumentationIDs OVER (ORDER BY i.ProductID, r.n)
        FROM INSERTED i
            CROSS APPLY (VALUES (1), (2), (3)) r(n)
            ;

    WITH Docs (ProductID, MarketingDocID, TechnicalDocID, InternalDocID)
    AS (
        SELECT ProductID, [1], [2], [3]
        FROM #DocIDs d
            PIVOT (MAX(DocID) FOR Num IN ([1], [2], [3])) pvt
    )
    UPDATE p
    SET
        p.MarketingDocumentationID = d.MarketingDocID
        , p.TechnicalDocumentationID = d.TechnicalDocID
        , p.InternalDocumentationID = d.InternalDocID
    FROM Product p
        JOIN Docs d ON d.ProductID = p.ProductID
        ;

END