2008-09-24 56 views
34

我在問好奇。基本上我的問題是,當你有一個數據庫需要一個行條目來讓事情像標誌一樣行事時,最佳實踐是什麼?一個很好的例子就是堆棧溢出的徽章或bugzilla中的操作系統字段。可以爲給定條目設置標誌的任何子集。數據庫中的標誌行,最佳實踐

通常,我做C和C++的工作,所以我的直覺反應是使用無符號整數字段作爲可以翻轉的一組位...但我知道這不是一個好的解決方案,原因有幾個。其中最明顯的是可擴展能力,對於我可以擁有多少標誌將會有一個硬性上限。

我也可以考慮一些其他解決方案,它們的規模會更好,但會有性能問題,因爲它們需要多個選項才能獲取所有信息。

那麼,什麼是「正確」的方式來做到這一點?

回答

26

如果你確實需要從一組關閉的標誌(例如stackoverflow徽章)中選擇一個無限選項,那麼「關係方式」就是創建一個標誌表和一個將這些標誌與你的目標實體相關聯的獨立表。因此,用戶,標誌和usersToFlags。

但是,如果空間效率是一個嚴重問題並且查詢能力不足,那麼未經簽名的掩碼幾乎可以工作。

+11

只對未簽名的掩碼發出警告。如果您必須編寫查詢來過濾設置了特定位的行,那麼當行數變大時,您的性能將受到嚴重影響,因爲where子句中的邏輯和/或操作無法有效地使用索引。 – JohnFx 2008-10-01 22:26:26

4

對於很多情況下,它取決於很多東西 - 比如你的數據庫後端。例如,如果您使用的是MySQL,則SET datatype正是您想要的。

基本上,它只是一個位掩碼,賦值給每個位。 MySQL最多支持64位值(意味着64個不同的切換)。如果你只需要8個,那麼每行只需要一個字節,這是非常可觀的節省。

如果您在一個字段中擁有超過64個值,那麼您的字段可能會變得更加複雜。您可能想擴展到BLOB數據類型,這只是MySQL沒有內在理解的原始位集。使用這種方法,您可以創建任意數量的位字段,MySQL很樂意將其視爲二進制,十六進制或十進制值,但是您需要。如果您需要超過64個選項,請根據您的應用程序創建儘可能多的字段。缺點是很難使該領域的人類可讀。 BIT datatype也限制爲64.

+0

不是我會做的,但它是位掩碼解決方案的一個很好的實現。 – 2008-09-24 01:23:31

28

一般來說,我避免了位掩碼字段。他們很難在將來閱讀,他們需要更深入的瞭解數據。

之前已經提出了關係解決方案。給你介紹的例子,我會創造這樣的事情(在SQL Server):


CREATE TABLE Users (
    UserId INT IDENTITY(1, 1) PRIMARY KEY, 
    FirstName VARCHAR(50), 
    LastName VARCHAR(50), 
    EmailAddress VARCHAR(255) 
); 

CREATE TABLE Badges (
    BadgeId INT IDENTITY(1, 1) PRIMARY KEY, 
    [Name] VARCHAR(50), 
    [Description] VARCHAR(255) 
); 

CREATE TABLE UserBadges (
    UserId INT REFERENCES Users(UserId), 
    BadgeId INT REFERENCES Badges(BadgeId) 
); 
+1

接受答案的好例子,謝謝。 – 2008-09-24 03:59:09

1

如果有不僅僅是幾個標誌的更多,或者可能在將來是如此,我將使用一個單獨的標誌表和它們之間的多對多表。

如果有少數的標誌,我永遠不會在WHERE中使用它們,我會使用SET()或位域或其他。它們易於閱讀和更緊湊,但是使用ORM查詢有時甚至更令人頭痛。

如果只有幾個標誌 - 並且只有成爲幾個標誌 - 那麼我只需製作幾個BIT/BOOLEAN/etc列。

2

如果標誌具有非常不同的含義並直接用於SQL查詢或VIEWS,那麼使用BOOLEAN類型的多列可能是一個好主意。

將每個標記放入一個額外的列中,因爲無論如何您都會分別讀取和修改它們。如果你想組的標誌,只是給他們列名的共同的前綴,即代替:

CREATE TABLE ... (
    warnings INTEGER, 
    errors INTEGER, 
    ... 
) 

你應該使用:

CREATE TABLE ... (
    warning_foo BOOLEAN, 
    warning_bar BOOLEAN, 
    warning_... 
    error_foo BOOLEAN, 
    error_bar BOOLEAN, 
    error_... BOOLEAN, 
    ... 
) 

儘管MySQL沒有布爾類型,你可以使用準標準TINYINT(1),並將其設置爲0或1.

1

如果您的數據庫支持此操作,我會推薦使用BOOLEAN數據類型。

否則,最好的方法是使用NUMBER(1)或等價物,並將限制有效值爲(0,1)的列設置檢查約束,如果需要的話可能爲NULL。如果沒有內置類型,則使用數字不會含糊不清使用字符列。 (真值是什麼?「T」或「Y」或「t」)

這樣做的好處是可以使用SUM()來計算TRUE行的數量。

SELECT COUNT(1), SUM(ActiveFlag) 
FROM myusers; 
3

一個非常關係方法

對於數據庫沒有設置類型,你可以打開一個新的表來表示一組爲每個標誌設置實體。

E.g.對於表「學生」你可以有表格「RegisteredStudents」,「SickStudents」,麻煩學生等。每個表將只有一列:student_id。如果你想知道哪些學生是「註冊」或「生病」,並且在每個DBMS中都以相同的方式工作,這實際上會非常快。