Posibles beneficios de almacenar múltiples valores en un campo de una fila en lugar de filas separadas


11

Durante nuestra última reunión semanal, una persona que no tiene experiencia en administración de bases de datos planteó esta pregunta:

"¿Habría un escenario que justifique el almacenamiento de datos en línea (cadena) en lugar de varias líneas?"

Supongamos una tabla llamada countryStatesdonde queremos almacenar los estados de un país; Usaré EE. UU. Para este ejemplo y no enumeraré todos los Estados en aras de la pereza.

Allí tendríamos dos columnas; uno llamó Countryy el otro llamó States. Como se discutió aquí , y propuesto por la respuesta de @ srutzky , PKserá el código definido por ISO 3166-1 alpha-3 .

Nuestra mesa se vería así:

+---------+-----------------------+-------------------------------------------------------+
| Country | States                | StateName                                             |
+---------+-----------------------+-------------------------------------------------------+
| USA     | AL, CA, FL,OH, NY, WY | Alabama, California, Florida, Ohio, New York, Wyoming |
+---------+-----------------------+-------------------------------------------------------+

Al hacerle esta misma pregunta a un desarrollador amigo, dijo que desde el punto de vista del tamaño del tráfico de datos, esto podría ser útil, pero no si necesitamos manipular estos datos. En este caso, tendría que haber una inteligencia sobre el código de la aplicación que podría transformar esta cadena en una lista (digamos que el software que tiene acceso a esta tabla necesita crear un cuadro combinado).

Llegamos a la conclusión de que este modelo no es muy útil, pero sospeché que podría haber una manera de hacerlo útil.

Lo que me gustaría preguntar es si alguno de ustedes ya vio, escuchó o hizo algo como esto de una manera que realmente funciona .


Ahora imagine que tiene una segunda tabla, "ventas", que tiene datos para cada venta que ocurrió junto con el código de estado en el que ocurrió la venta. ¿Cómo escribiría una consulta que genera un informe con columnas (StateName, TotalSalesAmount)? Difícil, ¿verdad?
zgguy

Exactamente. Tampoco estoy de acuerdo con este modelo. Nos atascamos en cualquier punto que necesitemos para recuperar cualquier tipo de datos (o datos útiles si lo desea).
Human_AfterAll

Un posible escenario podría ser almacenar variables. Tienda a;b;c, utilice la parte delantera para analizar la cadena a continuación, obtener a, b,c y llevar a cabo la ejecución de hacer algo con ellos, tal vez ?. Siente que podría satisfacer algún tipo de necesidad específica de esa manera ... Pensándolo bien, no. Siempre puede almacenar ID, unirse a sus tablas y crear una cadena concatenada que pueda enviar contenido a la FE ...
Nelz

Para ser justos (al menos para mí ;-), propuse usar los códigos de país de 2 caracteres :-) en esa otra respuesta .
Solomon Rutzky

2
Observe que nadie tiene reparos en almacenar el valor "Alabama" en una columna en lugar de tener una tabla separada con las columnas ESTADO, N y C para "el nombre del estado ESTADO tiene el enésimo carácter C". Porque 1. no tenemos la intención de consultar sobre los caracteres de los nombres o 2. no nos importa llamar a una función NTH_CHAR (N, S) que devuelve "el enésimo carácter de la cadena S" en cada fila con un nombre si lo hacemos . (Vs JOIN y otros operadores relacionales que eliminan algunas de esas filas mediante la tabla adicional). Lo mismo ocurre con los enteros y NTH_DIGIT (N, I). Siempre es una cuestión de juicio lo que en una base de datos particular es relacionalmente atómico.
Philip

Respuestas:


13

Para empezar, el título actual de la pregunta que se refiere a "almacenar datos como una cadena en lugar de columnas" es un poco confuso. Cuando se habla de almacenar datos como cadenas en lugar de otra cosa, eso generalmente se refiere a serializar todo en un formato de cadena en lugar de un tipo de datos adecuado / fuerte (por ejemplo, INTo DATETIME). Pero si se pregunta sobre el almacenamiento de datos como valores múltiples en un solo campo en lugar de filas separadas, eso es un poco diferente. Y para ser justos, aunque la concatenación de valores se hace más fácilmente con cadenas, también se puede hacer con INTy BINARYtipos, ya sea enmascarando bits o reservando de manera similar ciertas posiciones para tener diferentes significados. Dado que la segunda interpretación es sobre lo que realmente se está preguntando, basado en el texto de la Pregunta, abordemos eso.

En una palabra: No. Si está almacenando puntos de datos reales, solo traerá dolor (en términos de código y rendimiento) ya que es una complicación innecesaria. Si es un valor que solo se almacenará como una sola unidad, se actualizará como una sola unidad y nunca se desmontará dentro de la base de datos, entonces eso podría estar bien, ya que es más o menos análogo al almacenamiento de una imagen o PDF. De lo contrario, cualquier intento de analizar los datos invalidará el uso de cualquier índice (por ejemplo LIKE '%something%', usando CHARINDEX, o PATINDEX, o SUBSTRING, etc.).

Si necesita almacenar valores separados en un solo campo de una sola fila, entonces hay medios más apropiados para hacerlo: XML o JSON. Estos son formatos analizables ( XML / JSON ) y XML puede incluso indexarse . Pero, idealmente, estos datos se almacenarían en campos correctamente tipados para que puedan ser realmente útiles.

Y no olvide que el propósito de un RDBMS es almacenar datos de modo que puedan recuperarse y manipularse de la manera más eficiente posible, dentro de las restricciones impuestas por cumplir con ACID . Recuperar valores concatenados es suficientemente malo debido a la necesidad de analizar primero los valores, y eso no es indexable. Pero manipular a menudo significa reemplazar todo el blob solo para actualizar una parte (suponiendo que no exista un patrón para usar con una REPLACEfunción). El tipo de datos XML al menos permite XML DML para actualizaciones simplistas, aunque todavía no son tan rápidas como una simple actualización de datos modelados adecuadamente.

Además, dado un escenario como el que se muestra en la pregunta anterior, al concatenar todos los códigos de estado juntos, no podrá utilizar la clave externa (en cualquier dirección) esos valores.

¿Y qué pasa si los requisitos comerciales cambian con el tiempo y necesita rastrear propiedades adicionales de estos artículos? En términos de "estados", ¿qué pasa con las capitales, o la población, o un orden de clasificación, o cualquier otra cosa? Almacenado correctamente como filas, puede agregar más columnas para propiedades adicionales. Claro, puede tener múltiples niveles de datos analizables, como, por |StateCode,Capital,Population |StateCode,Capital,Populate|...suerte, cualquiera puede ver que el problema crece exponencialmente fuera de control. Por supuesto, este problema en particular se trata fácilmente con los formatos XML y JSON, y ese es su valor como se mencionó anteriormente. Pero aún necesitaría una muy buena razón para usar cualquiera de ellos como un medio inicial de modelado, ya que ninguno será tan eficiente como usar campos discretos en filas separadas.


9

De hecho, he usado algo así para un propósito muy limitado. Creamos una tabla de encabezados para los archivos de salida. Se construyeron específicamente y en su mayoría eran solo los encabezados de las columnas, pero no del todo. Entonces los datos se veían algo así

OutputType   OutputHeader
PersonalData Name|Address|City|State|Zip
JobInfo      Name|JobName|JobTitle

Esencialmente parecía que era una lista delimitada. Y en cierto sentido lo fue. Pero para nuestros propósitos era una sola cadena larga.

Ese es el truco aquí. Si nunca planea analizar la lista, entonces vale la pena guardarla. Sin embargo, si necesitará o incluso necesitará analizar la lista, entonces vale la pena el espacio y el tiempo extra para dividirla y guardarla en filas separadas.


1

Lo he usado una vez con una mesa bastante pequeña, por ejemplo:

CREATE TABLE t1 (
  ID number,
  some_feature   varchar2(100),
  valid_channels  varchar2(100));

CREATE TABLE channel_def (
  channel varchar2(100));

Y luego almacenar valores CRM,SMS,SELF-CAREenvalid_channel .

Toda la tabla tiene algo así como 10 registros. valid_channelcontiene valores que realmente deberían estar en una tabla de enlace que represente la relación de muchos a muchos. Mesat1 no se utilizará de forma intensiva, por lo que decidimos seguir este camino. Sin embargo, algunas políticas estuvieron involucradas en esta decisión (ver más abajo).

Pero en general lo evito, no es 3NF.

El lugar donde trabajo actualmente tiene docenas de tales columnas por todo el lugar. Su justificación es que facilita sus consultas: en lugar de unir tres tablas usando la tabla de enlace, pueden ir directamente a la tabla de definición usando LIKE. P.ej

SELECT * 
  FROM t1 
 INNER JOIN channel_def cd
    ON ','||t1.valid_channels||',' LIKE '%,'||cd.channel||',%';

Horrible + en Oracle deshabilita el uso del índice debido al inicio '%,'.


¿Cuál sería más lento: LIKEo una simple unión?
Human_AfterAll

Es mejor tener una unión en una columna que está indexada o al menos tiene una restricción referencial (FK). Además, las uniones generalmente se realizan en un PK de la otra tabla, que se indexa de forma predeterminada (al menos en Oracle). Si está preguntando sobre el caso particular en cuestión (ver arriba), el plan de ejecución probablemente diría que era el mismo, ya que era una tabla pequeña.
Robotron

@Human_AfterAll LIKEsería más lento, especialmente si los datos se modelan correctamente para usar un TINYINTcampo PK en channel_def. Entonces solo necesita comparar un solo byte entre las dos tablas. Aquí tiene que analizar la cadena, carácter por carácter (al menos hasta que se cumpla la condición), y está haciendo una búsqueda que no distingue entre mayúsculas y minúsculas (en función de que la tabla dada def no muestra una _BIN2clasificación utilizada). Esto también invalida los índices en SQL Server. Abordé esto en mi respuesta diciendo que el análisis no puede usar índices. Acabo de actualizar mi respuesta para hacerlo más claro.
Solomon Rutzky

1
@Human_AfterAll Diría que esta decisión de modelado se debió a la falta de experiencia y conocimiento (y a veces de pereza). Una combinación adicional es todo lo que se guarda, pero lo que se sacrifica es la capacidad de clave externa que evitaría la entrada de datos completamente falsos (incluso si no coincide con la LIKEcláusula y produce resultados extraños, aún puede causar otros problemas o al menos hacer la depuración más difícil / más larga). También hace que la actualización del valid_channelscampo sea más complicada. Esto no quiere decir que esto no funcione, simplemente no hay una buena razón para hacerlo.
Solomon Rutzky

"falta de experiencia": lo peor es que esta decisión de diseño en particular fue impuesta por un miembro del personal superior ...
Robotron

1

Esto se hizo aquí en SE. Como Marc Gravell escribe :

... Después de pensarlo y considerarlo, nos decidimos por una representación natural delimitada por una tubería (barra), con tuberías iniciales / finales, por lo que ".net c #" se convierte simplemente en "| .net | c # |". Esto tiene virtudes:

  • muy simple de analizar
  • La actualización masiva y la eliminación de etiquetas se pueden hacer con un reemplazo simple (incluidas las tuberías, para evitar reemplazar las coincidencias de etiqueta media)
  • ...

Este "nuevo formato" fue el siguiente paso del "formato anterior", que era un poco diferente y fue elegido para utilizar la función de búsqueda de texto completo de SQL Server, por lo que algunos de los beneficios no son relevantes si lo hace desde cero.

Presumiblemente no normalizaron completamente la cosa tanto por la cantidad de trabajo como por razones de rendimiento.


0

Bueno, un posible beneficio principal de usar cadenas y otros tipos de datos es enviarlos desde SQL Server a C #, C, C ++ (etc.) usando el SQLCLR cuando sea necesario un rendimiento absoluto. Incluso podría crear una vista o procedimiento almacenado para representar datos relacionales de manera no relacional, como lo ha hecho con su ejemplo anterior para este mismo propósito.

Ver este ejemplo:

http://aboutsqlserver.com/2013/07/22/clr-vs-t-sql-performance-considerations/

según Wikipedia: SQL CLR o SQLCLR (SQL Common Language Runtime) es una tecnología para alojar el motor de tiempo de ejecución de lenguaje común de Microsoft .NET dentro de SQL Server. El SQLCLR permite que el código administrado sea alojado y ejecutado en el entorno de Microsoft SQL Server.


2
Hola. ¿Puedes dar más detalles aquí? No estoy seguro de cómo esto es un beneficio de almacenar datos de manera no tradicional. En todo caso, es un beneficio de SQLCLR poder manejar mejor los formatos de datos alternativos si esos deben existir. Pero esa no es una razón para preferir un formato de datos alternativo. Como tal, realmente no creo que esto responda la pregunta.
Solomon Rutzky

El enlace del artículo explica los beneficios con los pros y los contras. Además, mencioné el almacenamiento de los datos de forma relacional y para fines de que el CLR los convierta en no relacionales con una vista o procedimiento almacenado. Su pregunta fue "¿Habría un escenario que justifique el almacenamiento de datos en línea (cadena) en lugar de varias líneas?" Y mi respuesta fue sí, aunque prefiero una vista o un procedimiento almacenado para interactuar con el CLR.
Sting

0

En mi opinión, la respuesta sería no. No he usado este enfoque y lo evitaría; no puedo pensar en una razón por la que seguiría esa ruta. Te estás inclinando hacia el mundo de JSON / NoSQL con una matriz.

Tuvimos opciones de diseño similares en un rol anterior por el cual el equipo de arquitectos quería tener un campo "Datos" que se delimitara y luego se convirtiera en binario. No fuimos por esa ruta al final por algunas razones.

Si tuviera que unirse a este tipo de datos, sería una experiencia fea. Actualizar elementos individuales de la cadena también sería desagradable.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.