Uso de una tabla de configuración de fila única en la base de datos de SQL Server. ¿Mala idea?

145

Al desarrollar una aplicación de carrito de compras, descubrí que necesitaba guardar la configuración y las configuraciones basadas en las preferencias y requisitos del administrador. Esta información puede ser cualquier cosa, desde información de la empresa, ID de cuenta de envío, claves API de PayPal, preferencias de notificación, etc.

Parece muy inapropiado crear una tabla para almacenar una sola fila en un sistema de base de datos relacional.

¿Cuál es la forma adecuada de almacenar esta información?

Nota: mi DBMS es SQL Server 2008 y la capa de programación se implementa con ASP.NET (en C #).

David Murdoch
fuente

Respuestas:

189

He hecho esto de dos maneras en el pasado: una tabla de una sola fila y una tabla de pares clave / valor, y hay enfoques positivos y negativos en cada enfoque.

Unica fila

  • positivo: los valores se almacenan en el tipo correcto
  • positivo: es más fácil tratar con el código (debido a lo anterior)
  • positivo: se pueden dar valores predeterminados a cada configuración individualmente
  • negativo: se requiere un cambio de esquema para agregar una nueva configuración
  • negativo: la tabla puede llegar a ser muy ancha si hay muchas configuraciones

Par clave / valor

  • positivo: agregar nuevas configuraciones no requiere un cambio de esquema
  • positivo: el esquema de la tabla es estrecho, con filas adicionales que se utilizan para nuevas configuraciones
  • negativo: cada configuración tiene el mismo valor predeterminado (¿nulo / vacío?)
  • negativo: todo debe almacenarse como cadenas (es decir, nvarchar)
  • negativo: cuando se trata de la configuración en código, debe saber de qué tipo es una configuración y emitirla

La opción de una sola fila es, con mucho, la más fácil para trabajar. Esto se debe a que puede almacenar cada configuración en su tipo correcto en la base de datos y no tener que almacenar los tipos de la configuración, así como sus claves de búsqueda en el código.

Una cosa que me preocupaba al usar este enfoque era tener varias filas en la tabla de configuración de fila única "especial". Lo superé por (en SQL Server):

  • Agregar una nueva columna de bits con un valor predeterminado de 0
  • crear una restricción de verificación para garantizar que esta columna tenga un valor de 0
  • creando una restricción única en la columna de bits

Esto significa que solo puede existir una fila en la tabla porque la columna de bits debe tener un valor de 0, pero solo puede haber una fila con ese valor debido a la restricción única.

adrianbanks
fuente
55
Hacemos lo de una sola fila en nuestra aplicación LOB. Todos los valores son del tipo correcto, lo que hace que su uso en la aplicación sea mucho más simple. Nuestro esquema se versiona junto con la aplicación, por lo que un cambio en la configuración de la configuración se gestiona como cualquier revisión de la aplicación.
DaveE
17
Fila única positiva: ¡Puede tener FK definido en algunas columnas!
wqw
8
Siempre puede hacer un par clave / valor con un identificador de tipo para determinar qué columna tiene el valor en su tipo de valor. Esto le brinda lo mejor de ambos mundos y puede usar un proceso almacenado para obtener el valor cuando lo necesite.
Middletone
19
Una cosa que realmente puede arruinar su día después de implementar la solución de una sola fila es cuando más tarde se le asigne "hagamos un seguimiento de la última vez que se modificó cada valor y quién lo cambió ..."
Dave Mateer,
66
Otra ventaja de la solución de una sola fila, que descubrí en un caso: tenía una aplicación creada para un cliente, con una tabla de una sola fila para "configuraciones". Más tarde obtuve otros dos clientes que querían usar la misma aplicación, pero deseaban configuraciones diferentes: todo lo que tenía que hacer era agregar un PK "client_id" a la tabla para mantener un conjunto de configuraciones por separado para cada cliente. (Esto es cuando te das cuenta de que estas "configuraciones" son realmente solo atributos para una entidad de nivel superior que aún no has modelado.)
Jeffrey Kemp
10

Debe crear una tabla con una columna para el tipo de información y el valor de la información (al menos). De esta forma, evita tener que crear nuevas columnas cada vez que se agrega una nueva información.

Otávio Décio
fuente
1
Simple y ordenado. Simplemente trabaje con una lista de pares de valores clave desde allí. Es posible que desee pensar un poco en los valores predeterminados, depende del contexto de uso ...
Paul Kohler
44
¿Por qué es un problema crear nuevas columnas? Sé que hay situaciones en las que los desarrolladores deben evitarlo debido a problemas políticos con la actualización de los esquemas SQL, pero no se menciona eso en la pregunta.
finnw
6

Una sola fila funcionará bien; incluso tendrá tipos fuertes:

show_borders    bit
admin_name      varchar(50)
max_users       int

Una desventaja es que requiere un cambio de esquema ( alter table) para agregar una nueva configuración. Una alternativa es la normalización, donde terminas con una tabla como:

pref_name       varchar(50) primary key
pref_value      varchar(50) 

Esto tiene tipos débiles (todo es un varchar), pero agregar una nueva configuración es solo agregar una fila, algo que puede hacer con solo el acceso de escritura a la base de datos.

Andomar
fuente
4

Personalmente, lo almacenaría en una sola fila si eso es lo que funciona. ¿Demasiado para almacenarlo en una tabla SQL? probablemente, pero no hay ningún daño real al hacerlo.

EJ Brennan
fuente
4

Como adivinó, y a excepción de las situaciones más simples, poner todos los parámetros de configuración en una sola fila tiene muchos inconvenientes. Es una mala idea ...

Una forma conveniente de almacenar la configuración y / o el tipo de información de preferencia del usuario es en XML . Muchos DBMS admiten el tipo de datos XML. La sintaxis XML le permite gastar el "lenguaje" y la estructura que describe la configuración a medida que esta configuración evoluciona. Una ventaja de XML es su soporte implícito para la estructura jerárquica, que permite, por ejemplo, almacenar pequeñas listas de parámetros de configuración sin tener que nombrarlos con un sufijo numerado. Un posible inconveniente del formato XML es que buscar y modificar estos datos en general no es tan sencillo como otros enfoques (nada complicado, pero no tan simple / natural)

Si desea permanecer más cerca del modelo relacional , el modelo Entidad-Atributo-Valor es probablemente lo que necesita, por lo que los valores individuales se almacenan en una tabla que normalmente se ve así:

EntityId     (foreign key to the "owner" of this attribute)
AttributeId  (foreign key to the "metadata" table where the attribute is defined)
StringValue  (it is often convenient to have different columns of different types
IntValue      allowing to store the various attributes in a format that befits 
              them)

Por el cual AttributeId es una clave foránea para una tabla donde se define cada Atributo posible ("parámetro de configuración" en su caso), con say

AttributeId  (Primary Key)
Name
AttributeType     (some code  S = string, I = Int etc.)
Required          (some boolean indicating that this is required)
Some_other_fields   (for example to define in which order these attributes get displayed etc...)

Finalmente, el EntityId le permite identificar alguna entidad que "posee" estos diversos atributos. En su caso, podría ser un UserId o incluso estar implícito si solo tiene una configuración para administrar.

Además de permitir que la lista de posibles parámetros de configuración crezca a medida que la aplicación evoluciona, el modelo EAV coloca los "metadatos", es decir, los datos pertenecientes al atributo, en tablas de datos, evitando así toda la codificación de los nombres de columna comúnmente vistos cuando los parámetros de configuración se almacenan en una sola fila.

mjv
fuente
3
Parece excesivo para la mayoría de los usos de una tabla de configuración.
JerryOL
Creo que la idea general detrás de este enfoque es genial. ¿Pero por qué XML? Simplemente elija un formato de intercambio de datos simple como JSON o YAML y puede tener las ventajas de las otras dos variaciones.
schlamar
1
EAV es relacional pero no está normalizado. Ciertamente, existen casos de uso (por ejemplo, los sistemas ORM parecen amarlos), pero el argumento de que los metadatos están en la base de datos para EAV no es una razón convincente para usarlos. Todos los RDBMS contienen metadatos en las tablas del sistema de todos modos que puede descubrir, por lo que las tablas de una sola fila también tienen metadatos en la base de datos. Los nombres de columna codificados tampoco son un problema. Si usa claves para entidades y atributos, entonces tiene una tabla de búsqueda codificada en otro lugar que los define (o peor, está en su capa de presentación).
Davos
3

Ciertamente no tiene que cambiar su esquema al agregar un nuevo parámetro de configuración en el enfoque normalizado, pero probablemente todavía esté cambiando su código para procesar el nuevo valor.

Agregar una "tabla alternativa" a su implementación no parece una gran compensación por la simplicidad y la seguridad de tipo del enfoque de una sola fila.

Dave Mikesell
fuente
2

Un par Clave y Valor es similar a una .Net App.Config que puede almacenar configuraciones de configuración.

Entonces, cuando desee recuperar el valor que podría hacer:

SELECT value FROM configurationTable
WHERE ApplicationGroup = 'myappgroup'
AND keyDescription = 'myKey';
rizalp1
fuente
1

Una forma común de hacer esto es tener una tabla de "propiedades" simétrica a un archivo de propiedades. Aquí puede almacenar todas las constantes de su aplicación, o cosas no tan constantes que solo necesita tener a mano.

Luego puede obtener la información de esta tabla según lo necesite. Del mismo modo, cuando encuentre que tiene otra configuración para guardar, puede agregarla. Aquí hay un ejemplo:

propiedad_entry_table

[id, scope, refId, propertyName, propertyValue, propertyType] 
1, 0, 1, "COMPANY_INFO", "Acme Tools", "ADMIN"  
2, 0, 1, "SHIPPING_ID", "12333484", "ADMIN"  
3, 0, 1, "PAYPAL_KEY", "2143123412341", "ADMIN"   
4, 0, 1, "PAYPAL_KEY", "123412341234123", "ADMIN"  
5, 0, 1, "NOTIF_PREF", "ON", "ADMIN"  
6, 0, 2, "NOTIF_PREF", "OFF", "ADMIN"   

De esta manera, puede almacenar los datos que tiene y los datos que tendrá el próximo año y que aún no conoce :).

En este ejemplo, su alcance y refId se pueden usar para lo que desee en el back-end. Entonces, si propertyType "ADMIN" tiene un ámbito 0 refId 2, usted sabe qué preferencia es.

El tipo de propiedad viene cuando, algún día, también necesita almacenar información no administrativa aquí.

Tenga en cuenta que no debe almacenar los datos del carrito de esta manera, o búsquedas para el caso. Sin embargo, si los datos son específicos del sistema , entonces ciertamente puede usar este método.

Por ejemplo: si desea almacenar su DATABASE_VERSION , usaría una tabla como esta. De esa manera, cuando necesite actualizar la aplicación, puede consultar la tabla de propiedades para ver qué versión de su software tiene el cliente.

El punto es que no desea usar esto para cosas que pertenecen al carrito. Mantenga su lógica empresarial en tablas relacionales bien definidas. La tabla de propiedades es solo para información del sistema.

Stephano
fuente
@finnw Estoy totalmente de acuerdo en que este método no debe usarse para búsquedas, especialmente cuando hay muchos tipos diferentes de búsquedas. Quizás entendí mal la pregunta. Parecía que necesitaba una tabla para constantes y propiedades del sistema. En ese caso, ¿por qué tener 10 tablas diferentes?
Stephano
nota: dijo "guardar configuraciones y configuraciones", no "Necesito guardar datos de carrito relacionales"
Stephano
Mi objeción a esto es que está omitiendo la tipificación de SQL y otros mecanismos de restricción para evitar actualizar el esquema SQL cuando agrega nuevos atributos. Como dice "datos que tendrá el próximo año y que aún no conoce". Sí, tendrá nuevos datos el próximo año, pero ¿qué le impide crear nuevas columnas SQL (escritas), CHECK y posiblemente restricciones de FOREIGN KEY para el momento en que se agrega?
finnw
Mi primer instinto es simplemente agregar estos datos a un archivo plano. Y tiene razón, este proceso de usar una tabla en su lugar evitará los mecanismos de restricción del DBMS. Sin embargo, diría que si te esfuerzas demasiado por seguir las técnicas adecuadas de la base de datos, te estás perdiendo el punto. Mira la primera respuesta; más votado en SO: stackoverflow.com/questions/406760/…
Stephano
2
Iría al par de valores clave, lo volcaría todo en un diccionario al inicio y lo ordenaría.
Paul Creasey
0

No estoy seguro de que una sola fila sea la mejor implementación para la configuración. Puede que sea mejor tener una fila por elemento de configuración con dos columnas (configName, configValue), aunque esto requerirá convertir todos sus valores en cadenas y viceversa.

De todos modos, no hay daño en usar una sola fila para la configuración global. Las otras opciones para almacenarlo en el DB (variables globales) son peores. Puede controlarlo insertando su primera fila de configuración, luego deshabilitando las inserciones en la tabla para evitar múltiples filas.

sideral
fuente
0

Puede hacer el par clave / valor sin conversiones agregando una columna para cada tipo principal y una columna que le indique en qué columna están los datos.

Entonces su mesa se vería así:

id, column_num, property_name, intValue, floatValue, charValue, dateValue
1, 1, weeks, 51, , ,
2, 2, pi, , 3.14159, , 
3, 4, FiscYearEnd, , , , 1/31/2015
4, 3, CompanyName, , , ACME, 

Utiliza un poco más de espacio, pero a lo sumo está utilizando unas pocas docenas de atributos. Puede usar una declaración de caso fuera del valor column_num para extraer / unir el campo derecho.

espintool
fuente
0

Lo siento, vengo años después. Pero de todos modos, lo que hago es simple y efectivo. Simplemente creo una tabla con tres () columnas:

ID: int (11)

nombre - varchar (64)

valor - texto

¡Lo que hago antes de crear una nueva columna de configuración, actualizarla o leer es serializar el "valor"! De esta manera estoy seguro del tipo (Bueno, php es :))

Por ejemplo:

b: 0; es para B OOLEAN ( falso )

b: 1; es para B OOLEAN ( verdadero )

yo: 1988; es para I NT

s: 5: "Kader"; es para una S TRING de 5 caracteres de longitud

Espero que esto ayude :)

Kader Bouyakoub
fuente
1
¿Por qué no simplemente crear una nueva columna para el tipo? i:1988Parece que está intentando contraer dos piezas de información en una sola columna.
maksymiuk
@maksymiuk SIMPLEMENTE porque una vez no serializado, obtienes el tipo exacto en lugar de usar un bucle después (si o cambiar) ... etc.
Kader Bouyakoub
sin necesidad de bucles o interruptores ni nada, en realidad eliminaría el paso de tener que analizar la información de cada fila, mientras que, si tuviera una columna adicional para el tipo, la información de tipo ya está disponible para el componente que extrae la información sin tener que hacer ningún paso más allá de sólo la consulta inicial
Maksymiuk
¿Te refieres a hacer algo así echo (int) $varpor un número entero y otros por otros tipos?
Kader Bouyakoub
0

Tener una columna clave como varchar y una columna de valor como JSON. 1es numérico mientras que "1"es una cadena. truey falseson ambos booleanos. Puedes tener objetos también.

kzh
fuente