2008-09-24 19 views
34

Lo estoy pidiendo por curiosidad. Básicamente mi pregunta es cuando tienes una base de datos que necesita una entrada de fila para tener cosas que actúen como banderas, ¿cuál es la mejor práctica? Un buen ejemplo de esto serían las insignias en el desbordamiento de la pila, o el campo del sistema operativo en bugzilla. Cualquier subconjunto de los indicadores se puede establecer para una entrada determinada.Banderas en filas de una base de datos, mejores prácticas

Normalmente, trabajo c y C++, así que mi reacción visceral es utilizar un campo entero sin signo como un conjunto de bits que se pueden voltear ... Pero sé que no es una buena solución por varias razones. El más obvio de los cuales es la capacidad de escala, habrá un límite superior difícil en la cantidad de banderas que puedo tener.

También puedo pensar en un par de otras soluciones que escalan mejor pero que tendrían problemas de rendimiento porque requerirían selecciones múltiples para obtener toda la información.

Entonces, ¿cuál es la forma "correcta" de hacer esto?

Respuesta

26

Si realmente necesita una selección ilimitada de un conjunto cerrado de banderas (por ejemplo, identificadores de stackoverflow), la "forma relacional" sería crear una tabla de banderas y una tabla separada que relacione esas banderas con sus entidades de destino. Por lo tanto, usuarios, banderas y usuariosToFlags.

Sin embargo, si la eficiencia del espacio es una preocupación seria y la capacidad de consulta no lo es, una máscara sin firmar funcionaría casi tan bien.

+11

Solo una advertencia en la máscara sin firmar. Si tiene que escribir consultas que filtran en filas donde se establece un bit específico, su rendimiento sufrirá mal cuando el número de filas sea grande porque las cláusulas lógicas y/o de operaciones en donde no pueden usar índices de manera eficiente. – JohnFx

4

En muchos casos, depende de muchas cosas, como el back-end de la base de datos. Si está utilizando MySQL, por ejemplo, el SET datatype es exactamente lo que quiere.

Básicamente, es solo una máscara de bits, con valores asignados a cada bit. MySQL admite valores de hasta 64 bits (es decir, 64 conmutadores diferentes). Si solo necesitas 8, solo se necesita un byte por fila, lo cual es un ahorro increíble.

Si honestamente tiene más de 64 valores en un solo campo, su campo puede ser cada vez más complicado. Es posible que desee expandir luego al tipo de datos BLOB, que es simplemente un conjunto de bits en bruto que MySQL no tiene una comprensión inherente de. Con esto, puede crear un número arbitrario de campos de bits que MySQL se complace en tratar como valores binarios, hexadecimales o decimales, como lo necesite. Si necesita más de 64 opciones, cree tantos campos como sea apropiado para su aplicación. El inconveniente es que es difícil hacer que el campo sea legible por humanos. El BIT datatype también está limitado a 64.

+0

No es lo que haría, pero es una buena implementación de la solución de máscara de bits. –

28

En general, evito los campos de máscara de bits. Son difíciles de leer en el futuro y requieren un conocimiento mucho más profundo de los datos para su comprensión.

La solución relacional se ha propuesto anteriormente. Teniendo en cuenta el ejemplo usted contorneó, me gustaría crear algo como esto (en 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

buen ejemplo de la respuesta aceptada, gracias. –

1

Si hay algo más que unas pocas banderas, o que puedan serlo en el futuro, voy a usar una por separado tabla de banderas y una tabla de muchos a muchos entre ellos.

Si hay un puñado de banderas y nunca las voy a usar en un DÓNDE, usaré un SET() o bitfield o lo que sea. Son fáciles de leer y más compactos, pero es un dolor de consulta y, a veces, incluso más de un dolor de cabeza con un ORM.

Si solo hay unas pocas banderas, y solo yendo a ser unas pocas banderas, entonces haré un par de columnas BIT/BOOLEAN/etc.

2

Si las banderas tienen significados muy diferentes y se usan directamente en consultas SQL o VIEWS, entonces usar varias columnas del tipo BOOLEAN podría ser una buena idea.

Ponga cada bandera en una columna adicional, ya que las leerá y las modificará por separado de todos modos. Si desea agrupar las banderas, acaba de dar sus nombres de columna de un prefijo común, es decir, en lugar de:

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

se debe utilizar:

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

Aunque MySQL no tiene un tipo booleano, que puede usar el TINYINT cuasi estándar (1) para ese propósito, y configurarlo solo a 0 o 1.

1

Recomendaría usar un tipo de datos BOOLEAN si su base de datos lo admite.

De lo contrario, el mejor enfoque es usar NUMBER (1) o equivalente, y poner una restricción de verificación en la columna que limita los valores válidos a (0,1) y quizás NULL si es necesario. Si no hay un tipo incorporado, usar un número es menos ambiguo que usar una columna de caracteres. (¿Cuál es el valor verdadero? "T" o "S" o "t")

Lo bueno de esto es que puede usar SUM() para contar el número de filas VERDADERAS.

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

un enfoque muy relacional

Para bases de datos sin el tipo de conjunto, que podría abrir una nueva tabla para representar el conjunto de las entidades para las que se establece cada bandera.

E.g. para una tabla "Estudiantes", podría tener las tablas "Estudiantes inscritos", "Estudiantes enfermos", Estudiantes problemáticos, etc. Cada tabla tendrá solo una columna: student_id. Esto realmente sería muy rápido si todo lo que quiere saber es qué estudiantes están "Registrados" o "Enfermos", y funcionaría de la misma manera en cada DBMS.

Cuestiones relacionadas