Por ejemplo, supongamos que quiero buscar un Usuario y todos sus números de teléfono y direcciones de correo electrónico. Los números de teléfono y correos electrónicos se almacenan en tablas separadas, un usuario para muchos teléfonos / correos electrónicos. Puedo hacer esto con bastante facilidad:
SELECT * FROM users user
LEFT JOIN emails email ON email.user_id=user.id
LEFT JOIN phones phone ON phone.user_id=user.id
El problema * con esto es que está devolviendo el nombre del usuario, el DOB, el color favorito y toda la otra información almacenada en la tabla del usuario una y otra vez para cada registro (los usuarios envían correos electrónicos a los registros de los teléfonos), presumiblemente consumiendo ancho de banda y disminuyendo la velocidad abajo los resultados.
¿No sería mejor si devolviera una sola fila para cada usuario, y dentro de ese registro hubiera una lista de correos electrónicos y una lista de teléfonos? También haría mucho más fácil trabajar con los datos.
Sé que puede obtener resultados como este usando LINQ o quizás otros marcos, pero parece ser una debilidad en el diseño subyacente de las bases de datos relacionales.
Podríamos evitar esto usando NoSQL, pero ¿no debería haber algún término medio?
¿Me estoy perdiendo de algo? ¿Por qué no existe esto?
* Sí, está diseñado de esta manera. Lo entiendo. Me pregunto por qué no hay una alternativa con la que sea más fácil trabajar. SQL podría seguir haciendo lo que está haciendo, pero luego podrían agregar una o dos palabras clave para realizar un pequeño procesamiento posterior que devuelva los datos en un formato anidado en lugar de un producto cartesiano.
Sé que esto se puede hacer en un lenguaje de script de su elección, pero requiere que el servidor SQL envíe datos redundantes (ejemplo a continuación) o que emita múltiples consultas como SELECT email FROM emails WHERE user_id IN (/* result of first query */)
.
En lugar de que MySQL devuelva algo similar a esto:
[
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"email": "[email protected]",
},
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"email": "[email protected]",
},
{
"name": "Jane Doe",
"dob": "1953-02-19",
"fav_color": "green",
"email": "[email protected]",
}
]
Y luego tener que agrupar un identificador único (¡lo que significa que también debo buscarlo!) Del lado del cliente para volver a formatear el conjunto de resultados como lo desea, solo devuelva esto:
[
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"emails": ["[email protected]", "[email protected]"]
},
{
"name": "Jane Doe",
"dob": "1953-02-19",
"fav_color": "green",
"emails": ["[email protected]"],
}
]
Alternativamente, puedo emitir 3 consultas: 1 para los usuarios, 1 para los correos electrónicos y 1 para los números de teléfono, pero luego los conjuntos de resultados de correo electrónico y número de teléfono deben contener el ID de usuario para que pueda volver a emparejarlos con los usuarios Anteriormente fui a buscar. Nuevamente, datos redundantes y posprocesamiento innecesario.
Respuestas:
En el fondo, en las entrañas de una base de datos relacional, son todas las filas y columnas. Esa es la estructura con la que una base de datos relacional está optimizada para trabajar. Los cursores trabajan en filas individuales a la vez. Algunas operaciones crean tablas temporales (nuevamente, deben ser filas y columnas).
Al trabajar solo con filas y devolver solo filas, el sistema puede manejar mejor la memoria y el tráfico de red.
Como se mencionó, esto permite que se realicen ciertas optimizaciones (índices, uniones, uniones, etc.)
Si uno quisiera una estructura de árbol anidada, esto requiere que uno extraiga todos los datos a la vez. Atrás quedaron las optimizaciones para los cursores en el lado de la base de datos. Del mismo modo, el tráfico a través de la red se convierte en una gran explosión que puede tomar mucho más tiempo que el lento goteo de fila por fila (esto es algo que ocasionalmente se pierde en el mundo web actual).
Cada idioma tiene matrices dentro de él. Estas son cosas fáciles para trabajar e interactuar con ellas. Al usar una estructura muy primitiva, el controlador entre la base de datos y el programa, sin importar el idioma, puede funcionar de manera común. Una vez que uno comienza a agregar árboles, las estructuras en el lenguaje se vuelven más complejas y más difíciles de atravesar.
No es tan difícil para un lenguaje de programación convertir las filas devueltas en alguna otra estructura. Conviértalo en un árbol o un conjunto hash o déjelo como una lista de filas sobre las que puede iterar.
También hay historia en el trabajo aquí. La transferencia de datos estructurados era algo feo en los viejos tiempos. Mire el formato EDI para tener una idea de lo que podría estar pidiendo. Los árboles también implican recursividad, que algunos idiomas no admitían (los dos idiomas más importantes de los viejos tiempos no admitían la recursividad: la recursión no entró en Fortran hasta F90 y, en la época, COBOL tampoco).
Y aunque los idiomas de hoy tienen soporte para la recursividad y los tipos de datos más avanzados, no hay realmente una buena razón para cambiar las cosas. Funcionan y funcionan bien. Los que están cambiando las cosas son las bases de datos nosql. Puede almacenar árboles en documentos en uno basado en documentos. LDAP (en realidad es antiguo) también es un sistema basado en árbol (aunque probablemente no sea lo que buscas). Quién sabe, quizás lo siguiente en las bases de datos nosql será uno que devuelva la consulta como un objeto json.
Sin embargo, las bases de datos relacionales "antiguas" ... están trabajando con filas porque eso es en lo que son buenos y todo puede hablarles sin problemas ni traducción.
De RFC 1925 - Las doce verdades de redes
fuente
Está devolviendo exactamente lo que solicitó: un conjunto de registros único que contiene el producto cartesiano definido por las uniones. Hay muchos escenarios válidos en los que eso es exactamente lo que desearía, por lo que decir que SQL está dando un mal resultado (y, por lo tanto, implica que sería mejor si lo cambiara) realmente arruinaría muchas consultas.
Lo que está experimentando se conoce como " desajuste de impedancia de objeto / relación " , las dificultades técnicas que surgen del hecho de que el modelo de datos orientado a objetos y el modelo de datos relacionales son fundamentalmente diferentes de varias maneras. LINQ y otros marcos (conocidos como ORM, Object / Relational Mappers, no por casualidad) no mágicamente "evitan esto"; solo emiten consultas diferentes. También se puede hacer en SQL. Así es como lo haría:
Itere la lista de usuarios y haga una lista de ID.
Y luego te unes al lado del cliente. Así es como LINQ y otros marcos lo hacen. No hay magia real involucrada; Solo una capa de abstracción.
fuente
Puede usar una función integrada para concatenar los registros juntos. En MySQL puede usar la
GROUP_CONCAT()
función y en Oracle puede usar laLISTAGG()
función.Aquí hay una muestra de cómo se vería una consulta en MySQL:
Esto devolvería algo como
fuente
El problema es que no estás siendo lo suficientemente selectivo. Pediste todo cuando dijiste
... y lo tienes (incluyendo DOB y colores favoritos).
Probablemente deberías ser un poco más (ejem) ... selectivo, y dijiste algo como:
También es posible que vea registros que parecen duplicados porque se
user
puede unir a variosemail
registros, pero el campo que distingue estos dos no está en suSelect
declaración, por lo que es posible que desee decir algo comoAdemás, me doy cuenta de que estás haciendo un
LEFT JOIN
. Esto unirá todos los registros a la izquierda de la unión (es decirusers
) a todos los registros a la derecha, o en otras palabras:( http://en.wikipedia.org/wiki/Join_(SQL)#Left_outer_join )
Entonces, otra pregunta es ¿realmente necesita una unión izquierda, o habría
INNER JOIN
sido suficiente? Son tipos muy diferentes de combinaciones.Si realmente desea que una sola columna dentro del conjunto de resultados contenga una lista que se genera sobre la marcha, puede hacerlo, pero varía según la base de datos que esté utilizando. Oracle tiene la
listagg
función .En última instancia, creo que su problema podría resolverse si reescribe su consulta cerca de algo como esto:
fuente
left join
ainner join
. En este caso, esto no reducirá las "repeticiones" de las que se queja el usuario; simplemente omitiría a aquellos usuarios que carecen de teléfono o correo electrónico. Casi ninguna mejora. también, al interpretar "todos los registros de la izquierda a todos los registros de la derecha" omite losON
criterios, que elimina todas las relaciones 'incorrectas' inherentes al producto cartesiano pero mantiene todos los campos repetidos.Las consultas siempre producen un conjunto de datos tabular rectangular (no irregular). No hay subconjuntos anidados dentro de un conjunto. En el mundo de los conjuntos, todo es un rectángulo puro no anidado.
Puedes pensar en una unión como poner 2 juegos uno al lado del otro. La condición "on" es cómo se combinan los registros de cada conjunto. Si un usuario tiene 3 números de teléfono, verá una duplicación de 3 veces en la información del usuario. La consulta debe generar un conjunto rectangular sin dientes. Es simplemente la naturaleza de unir conjuntos con una relación de 1 a muchos.
Para obtener lo que desea, debe usar una consulta separada como la descrita por Mason Wheeler.
El resultado de esta consulta sigue siendo un conjunto rectangular no dentado. Como es todo en el mundo de los sets.
fuente
Tienes que decidir dónde existen los cuellos de botella. El ancho de banda entre su base de datos y la aplicación suele ser bastante rápido. No hay razón para que la mayoría de las bases de datos no puedan devolver 3 conjuntos de datos separados dentro de una llamada y no se unan. Luego puedes unirte a todo en tu aplicación si quieres.
De lo contrario, desea que la base de datos reúna este conjunto de datos y luego elimine todos los valores repetidos en cada fila que son el resultado de las uniones y no necesariamente las filas mismas que tienen datos duplicados como dos personas con el mismo nombre o número de teléfono. Parece un montón de gastos generales para ahorrar en ancho de banda. Sería mejor concentrarse en devolver menos datos con un mejor filtrado y eliminar las columnas que no necesita. Porque Select * nunca se usa en la producción, eso depende.
fuente
De manera muy simple, no una sus datos si desea resultados distintos para una consulta de usuario y una consulta de número de teléfono, de lo contrario, como otros han señalado, el "Conjunto" o los datos contendrán campos adicionales para cada fila.
Emita 2 consultas distintas en lugar de una con una combinación.
En el procedimiento almacenado o consultas en línea sql craft 2 parametrizadas y devuelve los resultados de ambas. La mayoría de las bases de datos y los idiomas admiten múltiples conjuntos de resultados.
Por ejemplo, SQL Server y C # logran esta funcionalidad usando
IDataReader.NextResult()
.fuente
Te estás perdiendo algo. Si desea desnormalizar sus datos, debe hacerlo usted mismo.
fuente
El concepto de cierre relacional básicamente significa que el resultado de cualquier consulta es una relación que puede usarse en otras consultas como si fuera una tabla base. Este es un concepto poderoso porque hace que las consultas sean componibles.
Si SQL le permitiera escribir consultas que generen estructuras de datos anidadas, rompería este principio. Una estructura de datos anidada no es una relación, por lo que necesitaría un nuevo lenguaje de consulta o extensiones complejas de SQL para consultarlo más o unirlo a otras relaciones.
Básicamente, construiría un DBMS jerárquico sobre un DBMS relacional. Será mucho más complejo para un beneficio dudoso, y perderá las ventajas de un sistema relacional consistente.
Entiendo por qué a veces sería conveniente poder generar datos estructurados jerárquicamente desde SQL, pero el costo en la complejidad adicional en todo el DBMS para respaldar esto definitivamente no vale la pena.
fuente
Los Pls se refieren al uso de la función STUFF que agrupa varias filas (números de teléfono) de una columna (contacto) que se pueden extraer como una sola celda de valores delimitados de una fila (usuario).
Hoy lo usamos ampliamente pero enfrentamos algunos problemas de CPU y rendimiento elevados. El tipo de datos XML es otra opción, pero es un cambio de diseño, no una consulta de nivel uno.
fuente
STUFF
es similar al empalme. No estoy seguro de cómo se aplica eso a mi pregunta.