SQL Server 列舉(enum)的替代方式

在程式中列舉(enum)是一種常見的資料型別,會定義一些常數,限制資料只能是這些常數中的其中一個,方便表達狀態,避免無意義的數字。
以 C# 為例:
    
public enum OrderStatus
{
    Pending = 1, // 待處理訂單
    Processing = 2, // 訂單處理中
    Shipped = 3, // 訂單已出貨
    Delivered = 4, // 訂單已送達
}
    

在程式中要表達狀態就可以使用 OrderStatus 這個列舉來表達,非常容易閱讀:
    
OrderStatus orderStatus = OrderStatus.Pending;
    

但是在 Sql Server 中並沒有列舉(enum)可以使用,我們在儲存資料時就需要儲存「魔法數字」,例如上面的 1 代表「待處理訂單」,2 代表「訂單處理中」等等,但是我們在查詢資料時無法直接知道「3」這個數字到底代表什麼意思,也無法限制資料是否正確。
當然也可以直接儲存字串,但也一樣無法直接限制這個欄位的值,很可能會出現錯誤的參數(例如未定義的資料或是錯字),無法確認資料是否正確。

既然 SQL Server 沒有支援 enum,那我們也可以使用其他的方式解決。

限制欄位內容

假設我們有一個最簡單的訂單資料表,有 Status 欄位來儲存資料,可以使用 CHECK 來限制對應的值只能是指定的內容:
    
CREATE TABLE Orders(
    Id UNIQUEIDENTIFIER DEFAULT NEWID() PRIMARY KEY,
    Status NVARCHAR(50) NOT NULL CHECK (Status IN('Pending', 'Processing', 'Shipped', 'Delivered')),
);
    

如果要修改已存在的資料表也可以這樣定義:
    
ALTER TABLE Orders ADD CONSTRAINT CHK_OrderStatus CHECK (Status IN ('Pending', 'Processing', 'Shipped', 'Delivered'));
    

這樣在 Orders 資料表的 Status 欄位如果出現未定義的值時就會無法新增/更新,確保資料的正確性:
    
[23000][547] Line 1: The UPDATE statement conflicted with the CHECK constraint "CHK_OrderStatus". The conflict occurred in database "myDb", table "dbo.Orders", column 'Status'.
    

註:依照不同的資料庫定續,限制的值很可能不會區分大小寫

建立資料表儲存列舉值,綁定外鍵約束

還有另一個方法,就是建立另一個資料表,用來儲存列舉對應的內容,並且透過外鍵來限制資料是否正確。

建立用來儲存列舉的資料表:
    
CREATE TABLE OrderStatus
(
    Id     INT PRIMARY KEY,
    Status NVARCHAR(50) NOT NULL
);
    

在 OrderStatus 資料表中新增列舉內容:
    
insert into OrderStatus (Id, Status)
values  (1, N'Pending'),
        (2, N'Processing'),
        (3, N'Shipped'),
        (4, N'Delivered');
    

建立 Orders 資料表,並且將 Status 欄位與 OrderStatus 資料表綁定外鍵約束:
    
CREATE TABLE Orders
(
    Id     UNIQUEIDENTIFIER DEFAULT NEWID() PRIMARY KEY,
    Status INT NOT NULL REFERENCES OrderStatus(Id)
);
    

這樣在新增/更新資料時如果與「列舉」內容不符,就會拋出錯誤,確保資料正確:
    
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Orders__Status". The conflict occurred in database "myDb", table "OrderStatus", column 'Id'.
    

留言

張貼留言

如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com