2008-09-24 12 views
34

Pytam o to z ciekawości. Zasadniczo moje pytanie brzmi, kiedy masz bazę danych, która potrzebuje wpisu wiersza, aby mieć rzeczy, które działają jak flagi, jaka jest najlepsza praktyka? Dobrym tego przykładem mogą być odznaki przepełnienia stosu lub pole systemu operacyjnego w bugzilli. Dla danego wpisu można ustawić dowolny podzbiór flag.Flagi w wierszach bazy danych, najlepsze praktyki

Zwykle robię c i C++ pracy, więc moją reakcją jest użycie niepodpisanego pola liczby całkowitej jako zestawu bitów, które można odwrócić ... Ale wiem, że nie jest to dobre rozwiązanie z kilku powodów. Najbardziej oczywistym z nich jest skalowalność, nie będzie twardego górnego limitu na ile flag mogę mieć.

Mogę również wymyślić kilka innych rozwiązań, które będą lepiej skalowane, ale będą miały problemy z wydajnością, ponieważ wymagają wielu wyborów, aby uzyskać wszystkie informacje.

Co to jest "właściwy" sposób na zrobienie tego?

Odpowiedz

26

Jeśli naprawdę potrzebujesz nieograniczonej selekcji z zamkniętego zestawu flag (np. Znaczków stackoverflow), wówczas "metodą relacyjną" byłoby utworzenie tabeli flag i oddzielnej tabeli, która wiąże te flagi z twoimi jednostkami docelowymi. W ten sposób użytkownicy, flagi i usersToFlags.

Jeśli jednak efektywność przestrzenna jest poważnym problemem, a zdolność sprawdzania nie ma znaczenia, niepodpisana maska ​​działałaby równie dobrze.

+11

Po prostu ostrzeżenie na niepodpisanej masce. Jeśli musisz pisać zapytania filtrujące wiersze, w których ustawiony jest określony bit, twoja wydajność ucierpi, kiedy liczba wierszy stanie się duża, ponieważ logiczne i/lub operacje w których klauzule nie mogą efektywnie korzystać z indeksów. – JohnFx

4

W wielu przypadkach zależy to od wielu czynników - np. Od zaplecza bazy danych. Jeśli używasz MySQL, na przykład, SET datatype jest dokładnie tym, czego potrzebujesz.

Zasadniczo jest to po prostu maska ​​bitowa z wartościami przypisanymi do każdego bitu. MySQL obsługuje do 64-bitowych wartości (czyli 64 różnych przełączników). Jeśli potrzebujesz tylko 8, to zajmuje tylko bajt na wiersz, co jest całkiem niesamowite oszczędności.

Jeśli szczerze masz więcej niż 64 wartości w jednym polu, twoje pole może być bardziej skomplikowane. Możesz wtedy rozszerzyć na typ danych BLOB, który jest tylko nieprzetworzonym zbiorem bitów, których MySQL nie ma nieodłącznego pojęcia. Używając tego, możesz utworzyć dowolną liczbę pól bitowych, które MySQL z przyjemnością traktuje jako wartości binarne, szesnastkowe lub dziesiętne, jednak potrzebujesz. Jeśli potrzebujesz więcej niż 64 opcji, utwórz tyle pól, ile jest odpowiednich dla twojej aplikacji. Minusem jest to, że trudno jest uczynić pole człowiekiem czytelnym. Numer BIT datatype jest również ograniczony do 64.

+0

Nie to, co bym zrobił, ale jest to dobra implementacja rozwiązania Bit Mask. –

28

Ogólnie mówiąc, unikam pól bitmaskowych. W przyszłości są trudne do odczytania i wymagają znacznie głębszej znajomości danych do zrozumienia.

Rozwiązanie relacyjne zostało zaproponowane wcześniej. Biorąc pod uwagę przykład pan przedstawił, chciałbym stworzyć coś takiego (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

dobry przykład przyjętej odpowiedzi, dziękuję. –

1

Jeśli istnieje więcej niż tylko kilka flag, lub może być w przyszłości, będę używać oddzielnego tablica flag i stół między nimi.

Jeśli jest garstka flag i nigdy nie zamierzam ich używać w WHERE, użyję SET() lub bitfield lub cokolwiek innego. Są łatwe do odczytania i bardziej kompaktowe, ale stanowią kwerendę, a czasem nawet więcej bólu głowy z ORM.

Jeśli jest tylko kilka flag - i tylko będzie za kilka flag - wtedy zrobię kilka kolumn BIT/BOOLEAN/etc.

2

Jeśli znaczniki mają bardzo różne znaczenia i są używane bezpośrednio w zapytaniach SQL lub WIDOKACH, wówczas dobrym pomysłem może być użycie wielu kolumn typu BOOLEAN.

Umieść każdą flagę w dodatkowej kolumnie, ponieważ i tak będziesz czytać i modyfikować je osobno. Jeśli chcesz grupy flagi, dajcie ich nazw kolumnie wspólny prefiks, czyli zamiast:

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

należy użyć:

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

Choć MySQL nie posiada typ boolean, ci może użyć do tego celu quasi standardowego TINYINT (1) i ustawić go tylko na 0 lub 1.

1

Polecam używanie typu BOOLEAN, jeśli twoja baza danych to obsługuje.

W przeciwnym razie najlepiej jest użyć NUMBER (1) lub odpowiednika i umieścić ograniczenie sprawdzające na kolumnie, która ogranicza prawidłowe wartości do (0,1) i być może NULL, jeśli tego potrzebujesz. Jeśli nie ma wbudowanego typu, użycie numeru jest mniej niejednoznaczne niż użycie kolumny postaci. (Jaka jest wartość true? "T" lub "Y" lub "t")

Fajną rzeczą jest to, że możesz użyć SUM(), aby policzyć liczbę PRAWDZIWYCH wierszy.

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

Bardzo relacyjne podejście

przypadku baz danych bez ustawionego typu, można otworzyć nową tabelę do reprezentowania zbiór jednostek, dla których każda flaga jest ustawiona.

E.g. dla tabeli "Studenci" możesz mieć tabele "RegisteredStudents", "SickStudents", TroublesomeStudents itp. Każda tabela będzie miała tylko jedną kolumnę: student_id. Byłoby to bardzo szybkie, gdybyś chciał wiedzieć, którzy uczniowie są "zarejestrowani" lub "chorzy" i działaliby tak samo w każdym DBMS.