Sql-Server

SQL Server - 如何定義需要列名的預設值函式?

  • June 28, 2021

聲明預設列值時不允許使用列名。是否有解決方法來實現這樣的目標?

CREATE TABLE [dbo].[Tasks]
(
   [TaskId] INT IDENTITY(1,1) NOT NULL,
   [TaskName] NVARCHAR(255) NULL,
   [Priority] INT NOT NULL UNIQUE DEFAULT Max(Priority)+1, -- not allowed
)

要求是預設情況下最新任務獲得最低優先級(最高 int 值)。在任務在表中之後,可以對其進行優先級/取消優先級,例如與表中的任何其他任務交換優先級值。

我會使用一個序列來實現它。

該序列每次呼叫時都會生成一個唯一編號,它滿足您在列UNIQUE上定義的約束。[Priority]我們將使用它作為列值的預設值。

USE tempdb;

DROP TABLE IF EXISTS dbo.[Tasks];
DROP SEQUENCE IF EXISTS dbo.[tasks_priority_sequence]
GO

CREATE SEQUENCE dbo.[tasks_priority_sequence]
AS int
START WITH 1
INCREMENT BY 1
NO CYCLE
CACHE;
GO

CREATE TABLE dbo.[Tasks]
(
   [TaskId] int NOT NULL
       IDENTITY(1,1)
   , [TaskName] nvarchar(255) NULL
   , [Priority] int NULL 
       UNIQUE 
       DEFAULT (NEXT VALUE FOR dbo.[tasks_priority_sequence])
);
GO

INSERT INTO dbo.[Tasks] ([TaskName])
VALUES ('test1')
   , ('test2');

SELECT *
FROM dbo.[Tasks];

結果:

雖然使用序列會引入間隙,但您可以向列提供任意值這一事實向我表明,您不需要在[Priority]列中嚴格單調遞增的值。如果間隙對您來說是個問題,您可以使用NO CACHEpragma 重新定義序列,這將確保序列不會產生間隙。

這種方法的一個警告是,如果您手動為尚未由序列生成的列分配值,則序列將導致錯誤。您可以使用觸發器而不是序列來解決這個問題,但是使用基於觸發器的解決方案的性能不會像使用序列那樣線性。下面是一個會導致錯誤的程式碼範例:

--this works just fine
INSERT INTO dbo.[Tasks] (TaskName, [Priority])
VALUES ('test3', 3);

--this fails since the next value for the sequence already exists in the UNIQUE index.
INSERT INTO dbo.[Tasks] (TaskName)
VALUES ('test4')

錯誤:

消息 2627,第 14 層,狀態 1,第 36 行
違反 UNIQUE KEY 約束“UQ__Tasks__534DF97B192F9781”。
無法在對象“dbo.Tasks”中插入重複鍵。重複鍵值為 (3)。

引用自:https://dba.stackexchange.com/questions/294939