¿Por qué las bases de datos relacionales no admiten la devolución de información en un formato anidado?


46

Supongamos que estoy construyendo un blog en el que quiero tener publicaciones y comentarios. Por lo tanto, creo dos tablas, una tabla de 'publicaciones' con una columna de 'identificación' de enteros automáticos y una tabla de 'comentarios' que tiene una clave externa 'post_id'.

Luego quiero ejecutar lo que probablemente será mi consulta más común, que es recuperar una publicación y todos sus comentarios. Al ser bastante nuevo en las bases de datos relacionales, el enfoque que me parece más obvio es escribir una consulta que se vería así:

SELECT id, content, (SELECT * FROM comments WHERE post_id = 7) AS comments
FROM posts
WHERE id = 7

Lo que me daría la identificación y el contenido de la publicación que quiero, junto con todas las filas de comentarios relevantes empaquetadas ordenadamente en una matriz (una representación anidada como la que usarías en JSON). Por supuesto, las bases de datos relacionales y SQL no funcionan así, y lo más cerca que pueden estar es hacer una unión entre 'publicaciones' y 'comentarios' que devolverá una gran cantidad de duplicación innecesaria de datos (con la misma información de publicación repetida en cada fila), lo que significa que el tiempo de procesamiento se gasta tanto en la base de datos para poner todo junto como en mi ORM para analizar y deshacer todo.

Incluso si le indico a mi ORM que cargue con entusiasmo los comentarios de la publicación, lo mejor que puede hacer es enviar una consulta para la publicación, y luego una segunda consulta para recuperar todos los comentarios, y luego reunirlos del lado del cliente, que También es ineficiente.

Entiendo que las bases de datos relacionales son tecnología comprobada (demonios, son más antiguas que yo), y que se ha realizado una gran cantidad de investigación a lo largo de las décadas, y estoy seguro de que hay una muy buena razón por la cual ellas (y el SQL estándar) están diseñados para funcionar de la manera en que lo hacen, pero no estoy seguro de por qué el enfoque que describí anteriormente no es posible. Me parece la forma más simple y obvia de implementar una de las relaciones más básicas entre los registros. ¿Por qué las bases de datos relacionales no ofrecen algo como esto?

(Descargo de responsabilidad: principalmente escribo aplicaciones web usando almacenes de datos Rails y NoSQL, pero recientemente he estado probando Postgres, y en realidad me gusta mucho. No quiero atacar bases de datos relacionales, estoy perplejo).

No estoy preguntando cómo optimizar una aplicación Rails, o cómo solucionar este problema en una base de datos en particular. Me pregunto por qué el estándar SQL funciona de esta manera cuando me parece contradictorio y antieconómico. Debe haber alguna razón histórica por la cual los diseñadores originales de SQL querían que sus resultados se vean así.


1
No todas las ormas funcionan de esa manera. hibernate / nhibernate permite que se especifiquen combinaciones, y puede cargar con entusiasmo árboles de objetos completos desde una sola consulta.
nathan gonzalez

1
Además, aunque es un punto interesante de discusión, no estoy seguro de que esto sea realmente responsable sin tener una reunión con los muchachos de Ansi SQL
Nathan

@nathan: Sí, no todos. He estado usando Sequel, que le permite elegir el enfoque que prefiere para una consulta determinada ( documentos ), pero aún así fomentan el enfoque de consultas múltiples (por razones de rendimiento, supongo).

55
Debido a que un RDBMS está diseñado para almacenar y recuperar conjuntos, no está destinado a devolver datos para su visualización. Piense en ello como MVC: ¿por qué trataría de implementar la vista a costa de hacer que el modelo sea más lento o más difícil de usar? RDBMS ofrece beneficios que las bases de datos NoSQL no pueden (y viceversa): si lo está utilizando porque es la herramienta adecuada para resolver su problema, no le pedirá que devuelva los datos listos para mostrar.

1
Ellos ven para xml
Ian

Respuestas:


42

CJ Date entra en detalles sobre esto en el Capítulo 7 y el Apéndice B de SQL y Teoría Relacional . Tienes razón, no hay nada en la teoría relacional que prohíba que el tipo de datos de un atributo sea una relación en sí misma, siempre que sea el mismo tipo de relación en cada fila. Su ejemplo calificaría.

Pero Date dice que estructuras como esta están "generalmente, pero no invariablemente, contraindicadas" (es decir, una mala idea) porque las jerarquías de las relaciones son asimétricas . Por ejemplo, una transformación de estructura anidada a una estructura "plana" familiar no siempre se puede revertir para recrear el anidamiento.

Las consultas, restricciones y actualizaciones son más complejas, más difíciles de escribir y más difíciles de admitir para el RDBMS si permite atributos con valor de relación (RVA).

También enturbia los principios de diseño de bases de datos, porque la mejor jerarquía de relaciones no es tan clara. ¿Deberíamos diseñar una relación de Proveedores con un RVA anidado para las piezas suministradas por un Proveedor determinado? ¿O una relación de Partes con un RVA anidado para proveedores que suministran una Parte dada? ¿O almacenar ambos para facilitar la ejecución de diferentes tipos de consultas?

Este es el mismo dilema que resulta de la base de datos jerárquica y los modelos de bases de datos orientados a documentos . Finalmente, la complejidad y el costo de acceder a las estructuras de datos anidados impulsa a los diseñadores a almacenar datos de forma redundante para facilitar la búsqueda por diferentes consultas. El modelo relacional desalienta la redundancia, por lo que los RVA pueden trabajar en contra de los objetivos del modelado relacional.

Por lo que entiendo (no los he usado), Rel y Dataphor son proyectos RDBMS que admiten atributos con valores de relación.


Re comentar de @dportas:

Los tipos estructurados son parte de SQL-99, y Oracle los admite. Pero no almacenan múltiples tuplas en la tabla anidada por fila de la tabla base. El ejemplo común es un atributo de "dirección" que parece ser una sola columna de la tabla base, pero tiene más subcolumnas para calle, ciudad, código postal, etc.

Oracle también admite tablas anidadas , y estas permiten múltiples tuplas por fila de la tabla base. Pero no soy consciente de que esto es parte del SQL estándar. Y tenga en cuenta la conclusión de un blog: "Nunca usaré una tabla anidada en una declaración CREATE TABLE. ¡Dedica todo su tiempo a DESNESTARLOS para que vuelvan a ser útiles!"


3
No quisiera almacenar una relación dentro de otra: estarían en tablas separadas y desnormalizadas como de costumbre. Solo pregunto por qué este tipo de incrustación de resultados no está permitido en las consultas, cuando me parece más intuitivo que el modelo de combinación.
PreciousBodilyFluids

Los conjuntos de resultados y las tablas son de un tipo. Date los llama relaciones y relvars respectivamente (por analogía, 42 es un número entero, mientras que una variable xpuede tener el valor del número entero 42). Las mismas operaciones se aplican a las relaciones y los relvars, por lo que su estructura debe ser compatible.
Bill Karwin

2
SQL estándar admite tablas anidadas. Se llaman "tipos estructurados". Oracle es un DBMS que tiene esta característica.
nvogel

2
¿No es un poco absurdo argumentar que para evitar la duplicación de datos, debe escribir su consulta de manera plana y duplicadora de datos?
Eamon Nerbonne

1
@EamonNerbonne, simetría de operaciones relacionales. Por ejemplo, proyección. Si SELECCIONO algunos sub-atributos de un RVA, ¿cómo puedo aplicar una operación inversa al conjunto de resultados para reproducir la jerarquía original? Encontré la página 293 del libro de Date en Google Books, así que puedes ver lo que escribió: books.google.com/…
Bill Karwin

15

Algunos de los primeros sistemas de bases de datos se basaron en el modelo de base de datos jerárquica . Esto representaba datos en una estructura similar a un árbol con padres e hijos, como sugiere aquí. Los HDMS fueron reemplazados en gran medida por bases de datos basadas en el modelo relacional. Las principales razones de esto fueron que RDBMS podía modelar relaciones "de muchos a muchos" que eran difíciles para las bases de datos jerárquicas y que RDBMS podía realizar fácilmente consultas que no formaban parte del diseño original, mientras que HDBMS lo obligaba a consultar a través de rutas especificadas en tiempo de diseño.

Todavía hay algunos ejemplos de sistemas de bases de datos jerárquicos en la naturaleza, particularmente el registro de Windows y LDAP.

Amplia cobertura de este tema está disponible en el siguiente artículo


10

Supongo que su pregunta realmente se centra en el hecho de que, si bien las bases de datos se basan en una lógica sólida y establecen una base teórica y hacen un muy buen trabajo almacenando, manipulando y recuperando datos en conjuntos (bidimensionales) al tiempo que garantizan la integridad referencial, la concurrencia y muchas otras cosas, no proporcionan una característica (adicional) de enviar (y recibir) datos en lo que uno podría llamar formato orientado a objetos o formato jerárquico.

Luego afirma que "incluso si le ordeno a mi ORM que cargue con entusiasmo los comentarios de la publicación, lo mejor que puede hacer es enviar una consulta para la publicación, y luego una segunda consulta para recuperar todos los comentarios, y luego reunirlos del lado del cliente, que también es ineficiente " .

No veo nada ineficiente en enviar 2 consultas y recibir 2 lotes de resultados con:

--- Query-1-posts
SELECT id, content 
FROM posts
WHERE id = 7


--- Query-2-comments
SELECT * 
FROM comments 
WHERE post_id = 7

Yo diría que es (casi) la forma más eficiente (casi, ya que realmente no necesita las posts.idcolumnas y no todas comments.*)

Como Todd señaló en su comentario, no debe pedirle a la base de datos que devuelva datos listos para mostrar. Es el trabajo de la aplicación hacer eso. Puede escribir (una o algunas) consultas para obtener los resultados que necesita para cada operación de visualización para que no haya duplicación innecesaria en los datos enviados a través del cable (o el bus de memoria) desde la base de datos a la aplicación.

Realmente no puedo hablar de ORM, pero quizás algunos de ellos puedan hacer parte de este trabajo por nosotros.

Se pueden utilizar técnicas similares en la entrega de datos entre un servidor web y un cliente. Se utilizan otras técnicas (como el almacenamiento en caché) para que la base de datos (o la web u otro servidor) no se sobrecargue con solicitudes duplicadas.

Supongo que los estándares, como SQL, son mejores si permanecen especializados en un área y no intentan cubrir todas las áreas de un campo.

Por otro lado, el comité que establece el estándar SQL bien puede pensar lo contrario en el futuro y proporcionar estandarización para una característica adicional. Pero no es algo que se pueda diseñar en una noche.


1
Quise decir ineficiente en el sentido de que mi aplicación tiene que incurrir en la sobrecarga y el retraso de dos llamadas a la base de datos en lugar de solo una. Además de eso, ¿no es hacer una unión también devolver datos en un formato que está listo para mostrar? ¿O usando una vista de base de datos? También puede obviarlos simplemente ejecutando más consultas pequeñas y uniéndolas en su aplicación, si así lo desea, pero siguen siendo herramientas útiles. No creo que lo que propongo sea significativamente diferente de una combinación, aparte de ser más fácil de usar y más eficiente.

2
@Precious: no es necesario que haya más gastos generales para ejecutar múltiples consultas. La mayoría de las bases de datos le permiten enviar múltiples consultas en un solo lote y recibir múltiples conjuntos de resultados de una sola consulta.
Daniel Pryden

@PreciousBodilyFluids: el fragmento de SQL en la respuesta de ypercube es una consulta única que se enviaría en una sola llamada a la base de datos y devolvería dos conjuntos de resultados en una sola respuesta.
Carson63000

5

No puedo responder con una respuesta adecuada y discutida, así que siéntase libre de votarme en el olvido si me equivoco (pero corríjame para que podamos aprender algo nuevo). Creo que la razón es que las bases de datos relacionales se centran en el modelo relacional, que a su vez se basa en algo de lo que no sé nada llamado "lógica de primer orden". Lo que puede preguntar probablemente no encaja conceptualmente en el marco matemático / lógico sobre el que se basan las bases de datos relacionales. Además, lo que pides generalmente se resuelve fácilmente mediante bases de datos de gráficos, lo que da más pistas de que es la conceptualización subyacente de la base de datos lo que está en conflicto con lo que quieres lograr.


5

Sé que al menos SQLServer admite consultas anidadas cuando usas FOR XML.

SELECT id, content, (SELECT * FROM comments WHERE post_id = posts.id FOR XML PATH('comments'), TYPE) AS comments
FROM posts
WHERE id = 7
FOR XML PATH('posts')

El problema aquí no es la falta de soporte del RDBMS, sino la falta de soporte de tablas anidadas en tablas.

Además, ¿qué te impide usar una unión interna?

SELECT id, content, comments.*
FROM posts inner join comments on comments.post_id = posts.id
WHERE id = 7

Puede ver la unión interna como una tabla anidada, solo el contenido de los primeros 2 campos se repite una vez. No me preocuparía mucho el rendimiento de la combinación, la única parte lenta en una consulta como esta es la io desde la base de datos hasta el cliente. Esto solo será un problema cuando el contenido contenga una gran cantidad de datos. En ese caso, sugeriría dos consultas, una con select id, contenty una con una combinación interna y select posts.id, comments.*. Esto se escala incluso con varias publicaciones, ya que solo usaría 2 consultas.


Las preguntas abordan esto. O tiene que hacer dos viajes de ida y vuelta (no es óptimo) o debe devolver datos redundantes en las dos primeras columnas (tampoco es óptimo). Quiere la solución óptima (no es poco realista en mi opinión).
Scott Whitlock

Lo sé, pero no hay nada malo como una solución óptima. Lo único que puedo argumentar es dónde la sobrecarga sería mínima y de dónde dependería. Si desea la solución óptima, compare y pruebe diferentes enfoques. Incluso la solución XML puede ser más lenta dependiendo de la situación específica, y no estoy familiarizado con los almacenes de datos NoSQL, por lo que no puedo decir si tiene algo similar for xml.
Dorus

5

En realidad, Oracle admite lo que desea, pero debe ajustar la subconsulta con la palabra clave "cursor". Los resultados se obtienen a través del cursor abierto. En Java, por ejemplo, los comentarios aparecerían como conjuntos de resultados. Más sobre esto, vea la documentación de Oracle sobre "CURSOR Expression"

SELECT id, content, cursor(SELECT * FROM comments WHERE post_id = 7) AS comments
FROM posts
WHERE id = 7

1

Algunos admiten el anidamiento (jerárquico).

Si quisiera una consulta, podría tener una tabla que se haga referencia a sí misma. Algunos RDMS apoyan este concepto. Por ejemplo, con SQL Server se pueden usar expresiones de tabla comunes (CTE) para una consulta jerárquica.

En su caso, las Publicaciones estarían en el Nivel 0 y luego todos los comentarios estarían en el Nivel 1.

Las otras opciones son 2 consultas o Unirse con información adicional para cada registro devuelto (que otros han mencionado).

Ejemplo de jerárquico:

https://stackoverflow.com/questions/14274942/sql-server-cte-and-recursion-example

En el enlace anterior, EmpLevel muestra el nivel de anidamiento (o jerarquía).


No puedo encontrar ninguna documentación sobre subconjuntos de resultados en SQL Server. Incluso cuando se usa un CTE. Por conjunto de resultados me refiero a filas de datos con suficientes columnas fuertemente tipadas. ¿Puedes agregar referencias a tu respuesta?
SandRock

@SandRock: una base de datos enviará un único conjunto de resultados desde una consulta SQL. Al identificar los niveles en la consulta en sí, podría crear un conjunto de resultados jerárquico o anidado que debería procesarse. Creo que actualmente es lo más cerca que estamos de llegar a la devolución de datos que están anidados.
Jon Raynor

0

Lo siento, no estoy seguro de entender su problema exactamente.

En MSSQL solo puede ejecutar 2 declaraciones SQL.

SELECT id, content
FROM posts
WHERE id = 7

SELECT * FROM comments WHERE post_id = 7

Y devolverá sus 2 conjuntos de resultados simultáneamente.


La persona que hace la pregunta dice que esto es menos eficiente porque da como resultado dos viajes de ida y vuelta a la base de datos, y generalmente intentamos minimizar los viajes de ida y vuelta debido a los gastos generales. Quiere hacer un viaje de ida y vuelta y recuperar las dos mesas.
Scott Whitlock

Pero será un viaje de ida y vuelta. stackoverflow.com/questions/2336362/…
Biff MaGriff

0

Los RDBM se basan en la teoría y se adhieren a la teoría. Esto permite una buena consistencia y una confiabilidad matemáticamente probada.

Debido a que el modelo es simple y nuevamente basado en la teoría, facilita a las personas la optimización y muchas implementaciones. Esto es diferente a NoSQL donde todos lo hacen ligeramente diferente.

En el pasado hubo intentos de crear bases de datos jerárquicas, pero el IIRC (parece que no puede googlearlo) ha habido problemas (ciclos e igualdad vienen a mi mente).


0

Tienes una necesidad específica. Sería preferible extraer datos de una base de datos en el formato que desee, para que pueda hacer con él lo que desee.

Algunas cosas que las bases de datos no hacen tan bien, pero no es imposible construirlas para hacerlo de todos modos. Dejar el formato a otras aplicaciones es la recomendación actual, pero no justifica por qué no se puede hacer.

El único argumento que tengo en contra de su sugerencia es poder manejar este conjunto de resultados de una manera "sql". Sería una mala idea crear un resultado en la base de datos y no poder trabajar con él o manipularlo hasta cierto punto. Digamos que creé una vista construida de la manera que sugieres, ¿cómo la incluyo en otra declaración select? A las bases de datos les gusta tomar resultados y hacer cosas con ellos. ¿Cómo lo uniría a otra mesa? ¿Cómo compararía su conjunto de resultados con otro?

Entonces el beneficio de RDMS es la flexibilidad de sql. La sintaxis para seleccionar datos de una tabla está bastante cerca de una lista de usuarios u otros objetos en el sistema (al menos ese es el objetivo). No estoy seguro de que tenga sentido hacer algo completamente diferente. Ni siquiera los han llevado al punto de manejar código / cursores de procedimiento o BLOBS de datos de manera muy eficiente.


0

En mi opinión, se debe principalmente a SQL y a la forma en que se realizan las consultas agregadas: las funciones agregadas y la agrupación se ejecutan en grandes conjuntos de filas bidimensionales para devolver resultados. Así ha sido desde el principio y es muy rápido (la mayoría de las soluciones NoSQL son bastante lentas con la agregación y dependen del esquema desnormalizado en lugar de consultas complejas)

Por supuesto, PostgreSQL tiene algunas características de la base de datos orientada a objetos. De acuerdo con este correo ( mensaje ), puede lograr lo que necesita creando un agregado personalizado.

Personalmente, estoy usando marcos como Doctrine ORM (PHP) que hacen la agregación del lado de la aplicación y admiten características como la carga diferida para aumentar el rendimiento.


0

PostgreSQL admite una variedad de tipos de datos estructurados, incluidos Arrays y JSON . Con SQL o uno de los lenguajes de procedimiento integrados, puede crear valores con una estructura arbitrariamente compleja y devolverlos a su aplicación. También puede crear tablas con columnas de cualquiera de los tipos estructurados, aunque debe considerar cuidadosamente si está desnormalizando innecesariamente su diseño.


1
Esto no parece ofrecer nada sustancial sobre los puntos hechos y explicados en 13 respuestas anteriores
mosquito

La pregunta menciona específicamente a JSON y esta respuesta es la única que señala que JSON se puede devolver en consultas de al menos un RDBMS. Preferiría haber comentado la pregunta para decir que se basa en una premisa falsa y, por lo tanto, no puedo esperar ninguna respuesta definitiva. Sin embargo, StackExchange no me deja hacer eso.
Jonathan Rogers
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.