Comunidad Oracle Hispana

Nuevas Inquietudes sobre División Relacional. Segunda Parte.

Un año de estudio y me sigo sintiendo un principiante

Al poco tiempo de escribir la primera parte prometí una segunda parte que redacté y la dejé olvidada en una carpeta de mi disco duro por no considerarla lo suficientemente buena. Pasados ya unos meses, en los que creo haber avanzado en conocimientos, la encontré por casualidad, la releí y me dije a mi mismo, ¡no está tan mal! …

He retocado algunas cosas pero en esencia fue esto lo que expuse en su momento y lo que expongo a continuación. Soy de los que piensan que un profundo conocimiento del Algebra Relacional es necesario para ser un buen DBA. Así decía mi post hace varios meses con algunos retoques fruto de la experiencia.

"Hola amigos de la Comunidad Oracle Hispana, en mi anterior post sobre DIVISION RELACIONAL (división de tablas o relaciones) os manifesté algunas de mis inquietudes. Como principiante que era dije “He lanzado para aprender BBDD Oracle una ofensiva equivalente a la que lanzaron los Aliados en la 2ª Guerra Mundial en el día D…”. Han pasado ya algunos meses desde entonces, no he dejado de estudiar y ensayar prácticamente ningún día y ahora estoy, siguiendo este tontorrón símil histórico y sin querer ofender a nadie en “La Batalla de las Ardenas”…  Sí, he logrado  desembarcar en Normandía  pero la resistencia del adversario es tenaz y queda mucho combate hasta llegar a Berlín, es decir ser un DBA con sólidos y profundos conocimientos. Llevo ya casi un año de dedicación y ya veo que esto  va para largo...."

Pero... hablemos de División de Tablas

Me quejaba en mi anterior post (recomiendo para entender este artículo la lectura del anterior) de lo conceptualmente inalcanzable que era en un principio la idea de división de tablas. No obstante a lo largo del artículo ablandé el concepto y quedamos más cerca de lo que significa realmente dividir dos tablas o relaciones entre sí.

En este nuevo artículo os voy a aportar nuevas ideas y no creáis que he llegado hasta el final. No es sencillo y además para los expertos en matemáticas veréis que de alguna forma recurro al postulado (y los postulados decían nuestros profesores de matemáticas y filosofía no se discuten… JA JA JA)…


Hagamos antes una reflexión sobre la división aritmética… ¿Qué entendemos por división aritmética?
Si tenemos m elementos (dividendo) y n individuos (divisor), cuando dividimos m entre n con da un valor c (cociente). Entendemos de alguna manera que se trata de un reparto. Esos m elementos repartidos entre esos n individuos tocan a c elementos después del reparto equitativo que supone la división. Veamos esta división:

Todos sabemos hacer esta división ¿verdad?  De hecho aplicamos un conjunto de reglas que nos enseñaron en la niñez y generalmente no nos planteamos el porqué. Sencillamente lo hacemos, usamos números arábigos y una regla que fue descubierta dicen en la India ya hace algunos siglos y expandida en principio por los árabes. Su efectividad y uso en las transacciones comerciales hizo que se expandiese por todo el mundo.Era como la invención de la Rueda o el Cálculo Infinitesimal algo que la Humanidad necesitaba para progresar.

Cuando realizamos esta división, no razonamos ya nada, sencillamente aplicamos mecánicamente unos pasos que nos enseñaron más o menos con 8 o 9 años, es más hoy en día usamos una calculadora y hay que decir como dato curioso que algunos adultos se quejan de que se les han olvidado las reglas aritméticas y la peor de recordar, creo yo, es precisamente la división.

Dividamos ahora tablas entre sí en vez de números

Abramos la mente a algo nuevo. Dividamos ahora tablas entre sí  en vez de números…  Saquemos el cociente entre la tabla Coches y la tabla Colores;    Coches : Colores

A efectos de reparto, con la misma facilidad que interpretábamos la división aritmética  ahora interpretemos la división entre ambas tablas…  El resultado de dividir Coches entre Colores (Coches:Colores) es una tabla  con una única columna; Modelo  que contiene  los modelos de la tabla Coches que tienen combinación con todos y cada uno los colores de la tabla Colores. Esto ya quedó claro en mi primer post y mostré paso a paso que así era y después de una profunda reflexión lo acepté.  También debemos recordar que la división de tablas resolvía preguntas del tipo ALL. …

Imaginemos las tablas AUTORES y EDITORIALES. ¡Ojo entre ellas hay una relación M:N! Por eso escribo la tabla que cruza autores con editoriales y atributos de la relación muchos a muchos. Recordemos un Autor colabora con varias Editoriales y las Editoriales colaboran con varios Autores. Relación muchos a muchos.

AUTORES (AutorID, Nombre, Provincia, …etc).
EDITORIALES (EditorialID, Editorial, País, etc).
AUTORES_EDITORIALES(AutorID , EditorialID , ISBN, Fecha, …)

¿Cómo podría saber yo qué autores han editado en todas (ALL) las Editoriales descritas en la tabla EDITORIALES?  Pregunta totalmente análoga a: ¿Cuántos Modelos  tienen todos (ALL) los colores de la tabla Colores?
Estoy seguro que os queda clara esta extrapolación.

SOLUCION: En este caso sería el cociente entre la tabla que describe la relación, AUTORES_EDITORIALES, entre EDITORIALES. Al igual que la tabla COCHES listaba los coches con los colores, la tabla AUTORES_EDITORIALES (en este caso la tabla que implementa la relación muchos a muchos) lista los autores con las editoriales con las que ha trabajado. Un impulso irreflexivo podría llevarnos a dividir AUTORES entre EDITORIALES y entonces yo tengo que hacer de maestro de primaria y deciros  ¡HAY QUE RAZONAR! eso sí, sin castigos… bueno, leer este post quizá sea ya un castigo, eso sí autoimpuesto.

¿Os dais cuenta? Ambas son preguntas de tipo ALL y se cumplen los requisitos para que ambas tablas sean divisibles.

Al igual que en la niñez admití lo que era la división (reparto) y me explicaron una regla. Yo ahora admito que la división relacional entre tablas es lo que es también una forma de reparto, citado anteriormente  y se hace así, esta es la regla:

Tabla1:Tabla2  =  πc(Tabla1) - πcc(Tabla1) x Tabla2 – Tabla1)

coches:colores  =  πc(coches) - πcc(coches) x colores – coches)

autores_editoriales:editoriales = πc(autores_editoriales) - πcc(autores_editoriales) x editoriales – autores_editoriales)

Lo que significa cada cosa (c diferencia de atributos, π  proyecciones, x producto cartesiano, resta relacional, etc.) quedó claro en anterior post.

No estoy ya en la niñez (aunque llevo un niño dentro todavía)  y aunque sea ya un poco “cucho” (palabra que me ha enseñado con reservas mi mujer que es colombiana) admito que lo que he expresado antes es la división de tablas y que la regla, eso que aplico y no discuto,  es la expresada en la fórmula anterior.

No voy a discutirlo. ¡¡¡YA LO ENTIENDO!!!  LO TOMO COMO LA BASE DE PROBLEMAS QUE PUEDO RESOLVER.  Me siento igual de cómodo dividiendo  11335 caramelos entre 5 niños  para que toquen a 227 caramelos. Que dividiendo la tabla Coches entre la tabla Colores para que me dé una tabla Resultado que contiene una única columna llamada Modelo con una única fila cuyo valor es Seat porque los Modelos Seat tenían todos los colores presentes en la tabla Colores.

Quería saber qué Modelos tienen o combinan con todos (pregunta tipo ALL) los Colores de la tabla Colores… quería saber qué autores, de la tabla AUTORES_EDITORIALES, han editado en todas  las editoriales descritas en la tabla EDITORIALES.

OH MY GOD!!     ¡¡YA LO ENTIENDO!!

¿Cómo se hace esto con SQL?

Voy a usar la cláusula SQL WITH  por su bondad en crear tablas temporales. También  intentar convertir la expresión anterior (regla de la división entre tablas)

Tabla1:Tabla2  =  πc(Tabla1) - πcc(Tabla1) x Tabla2 – Tabla1)

coches:colores  =  πc(coches) - πcc(coches) x colores – coches)

a SQL tal y como está.  Tabla1=coches,  Tabla2=colores

c=modelo

πc(coches) x colores = SELECT  distinct a.modelo , b.color FROM  coches a, colores b  -> La tabla que llamaremos  primtb.

πc(coches) x colores – coches = SELECT  * FROM primtb  MINUS SELECT  * FROM  coches  -> La tabla que llamaremos  seguntb.

coches:colores = πc(coches) - πc(πc(coches) x colores – coches) = SELECT modelo FROM coches MINUS SELECT modelo FROM seguntb -> El resultado coches:colores

La consulta, con las consideraciones anteriores, nos puede quedar de estas variadas maneras basado todo en la misma idea.

La consulta tal cual, abajo:

 WITH primtb AS (

SELECT  distinct a.modelo , b.color FROM coches a, colores b
),
seguntb AS (
SELECT  * FROM primtb MINUS SELECT  * FROM  coches
)
SELECT modelo FROM coches MINUS SELECT modelo FROM seguntb;

 

La consulta generando la tabla cociente: modelos_todos_los_colores

 CREATE TABLE MODELOS_TODOS_LOS_COLORES AS

WITH primtb AS (

SELECT  distinct a.modelo , b.color FROM coches a, colores b

),

seguntb AS (

SELECT  * FROM primtb MINUS SELECT  * FROM  coches

)

SELECT modelo FROM coches MINUS SELECT modelo FROM seguntb;

Creando una vista suponiendo que estas dos tablas tienen muchas inserciones tanto de coches y de colores disponibles y por tanto con una simple sentencia select queremos saber qué modelos tenemos con todos los colores disponibles

CREATE VIEW  VMOD_TODOS_COLORES AS

WITH primtb AS (

SELECT  distinct a.modelo , b.color FROM coches a, colores b

),

seguntb AS (

SELECT  * FROM primtb  MINUS SELECT  * FROM  coches

)

SELECT modelo FROM coches MINUS SELECT modelo FROM seguntb

WITH READ ONLY;

 

Sql> SELECT * FROM  vmod_todos_colores;

 

MODELO

 ------------------

Seat

Aqui tenéis una visión del resultado desde el SQL Developer.

Ya hemos crecido. Curso siguiente… ¡¡Dividamos “por más de una cifra”!!

Ahora el desafío es más grande pero realmente no debe entrañar dificultad si nos atenemos  a la definición (la cual aplicamos como una “regla”) para obtener la división de la tabla  R entre la tabla S.

Ahora la tabla denominador tiene más de una columna y más de una fila. La pregunta sería  qué filas de las columnas  A, B, C de la tabla R combinan con todas (ALL) las filas de las columnas D, E, F descritas en la tabla S. El gráfico adjunto (tomado de Internet) nos da la solución, son las tuplas  (a, b, c) y (d, a, f). El gráfico mostrado abajo no ayuda a comprender mejor lo que se quiere y la solución.

c=A,B,C (diferencia de atributos entre R y S).

 πc(R) x S = SELECT  distinct x.A,  x.B,  x.C , y.D,  y.E,  y.F    FROM  R  x,  S  y  -> La tabla que llamaremos  primtb.

πc(R) x S – R = SELECT  * FROM primtb  MINUS SELECT  * FROM  R  -> La tabla que llamaremos  seguntb

R : S  = πc(R) - πc(πc(R) x S – R) = SELECT  A, B, C FROM R MINUS SELECT A,B,C FROM seguntb -> resultado, la division entre R y S.

Algunas inquietudes mas de un lego en la materia

Bueno, pues ya está hecho el trabajo. Quedan muchos enigmas por resolver y muchas cosas en las que pensar sobre este tema. Aquí van algunas inquietudes, sobre algunas ya he meditado,  sobre otras todavía no, aquí van:

  • ¿Qué diría nuestro experto en tuning y optimización SQL Javier Morales Carreras sobre esos descarados productos cartesianos que he invocado?  Seamos realistas los CROSS JOIN no tienen precisamente muy buena fama. ¿Una cosa seria la definición formal de la división relacional y otra su implementación a base de ingeniosas consultas y subconsultas, evitando productos cartesianos?
  • ¿Los conceptos de elemento neutro, elemento simétrico, división por “cero” y resto tienen aquí sentido?
  • ¿Tiene sentido y utilidad hablar de divisibilidad y múltiplos de tablas?
  • ¿Puedo despejar algún término de esta expresión?
    Tabla1:Tabla2  =  πc(Tabla1) - πc(πc(Tabla1) x Tabla2 – Tabla1)
    Y si es así ... ¿Cuál es la contrapartida de la división? Será ignorancia pero yo NO veo que sea el producto cartesiano.
    ¿Alguien de la COH puede despejar por mi la Tabla1?

Tanto este post como el anterior he pretendido expresar por escrito unas inquietudes surgidas durante el aprendizaje. Mi siguiente post será sobre el Diccionario de Datos. Creo que está mal presentado a los que empezamos (no digo para nada que esté mal explicado sino al contrario) en muchos libros y muchos tutoriales. Creo que el no abordar bien desde el principio este tema, como trabajar con el Diccionario de Datos, puede retrasar el avance...  es una cuestión de como enfocarlo para hacerlo abarcable.

Le pregunté una vez a un formador de Oracle ¿Cómo  puedo abordar el Diccionario de Datos si me resulta inconmensurable? ¿Qué estoy haciendo mal para que me resulte, infinito, memorístico, disperso y mentalmente inabarcable?

Su respuesta, ademas de breve, a mi entender pudo haber sido mejor y este muro será motivo de mi siguiente post. Quizá yo tampoco fui el mejor alumno  ja ja ja.

Gracias por leerme.

Un abrazo a toda la Comunidad Oracle Hispana y Feliz Navidad.

Visitas: 265

Comentario de Isabel C el diciembre 16, 2015 a las 6:54am
Me ha gustado el articulo a pesar de su complejidad. Me gusta que hagas sencillo un concepto áspero y la forma de llevar de la mano al lector. Pero tengo un pregunta para tí.
En la generación de la tabla cociente... ¿ no crees que sería mejor hacerlo vía un procedimiento PL/SQL de tal manera que implementes una evaluación en cortocircuito para salvar la contingencia de que en ese momento la tabla denominador estuviese vacía y así eludieses una temida teórica división por cero que tanto temes en tu reflexión final?
 
Comentario de Javier Morales Carreras Hace 6 horas

Aunque tarde, aquí va mi opinión.

Dado que la "división de tablas" no es un operador SQL en sí, todo el conjunto de formulas para resolverlo (desde el punto de vista de tuning) me parecen poco óptimas. Por supuesto, generar un producto cartesiano para realizar un MINUS, seguro que conllevará un coste alto, pero si es necesario realizar una división de tablas por imperativo legal, y después de nuestra manifiesta protesta y descargo de responsabilidades sobre lo rápido o lento que llegue a ejecutarse ese bloque de código, pues si ha de hacerse, se hará.

En el libro de Optimización SQL en Oracle analizo los costes de UNION, UNION ALL, INTERSECT y MINUS, que sí son operadores nativos, y para aportar algo a tu post, decir que cuentas con un elemento "extra", que son las ordenaciones que estos procesos realizan internamente (si se trata de millones de filas, prepárate a esperar). Para ser concretos: UNION, INTERSECT y MINUS implican ordenar el conjunto de resultados (tal como muestran los planes de ejecución), y es necesario poner prudencia a su uso.

Sobre lo que dice Isabel C, me basaría en la regla no escrita de:

"Si puedes hacerlo en SQL, no lo hagas en PL/SQL,

Si puedes hacerlo en PL/SQL, no lo hagas en Java (u otros)"

Y para los que con tu artículo aun quieran seguir indagando algo más, aquí dejo un PDF muy majo.

https://vadimtropashko.files.wordpress.com/2007/02/ch3.pdf

Un saludo a todos!

Comentario

¡Tienes que ser miembro de Comunidad Oracle Hispana para agregar comentarios!

Participar en Comunidad Oracle Hispana

© 2017   Creado por Fernando Garcia.   Tecnología de

Insignias  |  Informar un problema  |  Términos de servicio