Anda di halaman 1dari 7

Operadores Derivados del Álgebra Relacional

Carlos E. Cuesta.
Departamento de Informática (ATC, CCIA, LSI)

El lenguaje del Álgebra Relacional consta de seis operadores básicos, tres binarios y tres mona-
rios: la unión (R ∪ S), la diferencia (R − S), el producto cartesiano (R × S), la proyección
(πX (R)), la selección (σF (R)) y el renombrado (ρX (R)). Codd demostró que este álgebra
–es decir, este conjunto de operadores– es relacionalmente completo, lo que significa que cual-
quier acceso a la información contenida en una Base de Datos Relacional puede ser expresado
mediante una combinación de dichos operadores.
Sin embargo, estos operadores no expresan directamente una serie de operaciones habituales
en los procesos de consulta. Dada su frecuencia, resulta rentable definir operadores especı́ficos
para dichas operaciones, de modo que se puedan utilizar como si fueran primitivos. Por supues-
tos, estos nuevos operadores pueden ser especificados a partir de los básicos (debido precisamente
a la completud relacional de los mismos), por lo que reciben el nombre de operadores derivados.
Todos ellos son binarios, y se describen brevemente a continuación.

Intersección
Es el más sencillo e inmediato. Se representa con la sintaxis R ∩ S. Su semántica es análoga
a la del operador homónimo de la Teorı́a de Conjuntos. Es decir, para dos relaciones R y S
del mismo grado y con los mismos atributos, devuelve el conjunto de tuplas comunes a ambas
relaciones. Su descripción en función de los operadores básicos es inmediata:

R ∩ S ≡ R − (R − S) ≡ S − (S − R)

Intuitivamente equivale a la operación que extrae todas las tuplas comunes a dos relacio-
nes dadas, análoga por tanto a una conjunción lógica (∧).

Cociente
Es el más complejo, pero también el más potente de los operadores del Álgebra Relacional. Se
expresa mediante la sintaxis A ÷ B, notación tradicionalmente asociada al cociente aritmético.
De hecho, al igual que ocurre en el dominio de los números, el cociente será la operación inversa
al producto (cartesiano), aunque la correspondencia no resulta obvia a primera vista.
La operación se define para dos relaciones R y S, de grados r y s respectivamente, tales que
r > s; y donde se cumple además que S 6= ∅. En tal caso, se dice que el cociente está definido,

1
esto es, que R es divisible entre S 1 . Entonces, el cociente T de esas dos relaciones (T = R÷S) es
el conjunto de las tuplas t de grado (r − s), tales que para toda tupla u de S, la tupla compuesta
ht, ui está en R. Es decir:

T = R ÷ S; hti ∈ T ⇐⇒ ∀hui ∈ S : ht, ui ∈ R


En resumen, T (“el cociente”) es una relación consistente en una serie de tuplas que reúnen
lo que les falta a las tuplas de S (“el divisor”) para obtener algunas tuplas que estaban origi-
nalmente en R (“el dividendo”). Más exactamente, cada tupla t de T contiene información que
complementa a todas las tuplas de S de modo que, combinadas, expresan parte de la información
de R. El matiz indicado por el uso del cuantificador universal (∀) en la expresión anterior resulta
fundamental: significa que para que cierta tupla-cociente t esté en T , la información que ésta
contiene ha de aparecer en tantas tuplas-dividendo (de R) como tuplas-divisor haya en S.
Como se ha dicho, el cociente y el producto cartesiano son operaciones inversas, aunque no
hay una correspondencia total entre ellas. Esto se debe al hecho de que al realizarse el cociente
se pierde información, y por tanto la reversibilidad. En primer lugar, cualquier tupla de R cuya
“mitad derecha” no coincida con ninguna tupla de S es ignorada por completo. Pero más aún, si
en R existen tuplas cuya “mitad derecha” coincide con alguna tupla de S, pero no con
todas, tampoco éstas son mencionadas en T 2 . Sin embargo, estas aparentes “pérdidas” de
datos son necesarias, ya que en realidad esta información no es relevante y conservarla provocarı́a
la generación de información falsa. De hecho, de no hacerse de este modo, el producto cartesiano
no serı́a, de hecho, el inverso del cociente.
En efecto, se puede ver que si T = R ÷ S, entonces se cumple que T × S ⊆ R; el producto
cartesiano de cada tupla de T por todas las tuplas de S da siempre como resultado una tupla de
R. Si se hubiesen relajado las restricciones del cociente, en el producto cartesiano se obtendrı́an
tuplas espurias, que nunca estuvieron presentes en R. En tal caso, no se podrı́a afirmar siquiera
la inclusión, y en la mayorı́a de los casos se tendrı́a una desigualdad estricta (T 0 × S 6= R).
Por supuesto, al ser un operador derivado, el cociente puede ser expresado en función de los
operadores básicos. Su definición recuerda en cierto modo a la de la intersección, y se basa, como
ésta, en la semántica de la diferencia. Sin embargo, ahora se trata con relaciones de distintos
grados, por lo que resulta más complicada, ya que requiere el concurso de varias proyecciones.
Su descripción consiste en la equivalencia siguiente:

R ÷ S ≡ π1,2,...,(r−s) (R) − π1,2,...,(r−s) ((π1,2,...,(r−s) (R) × S) − R)


En ella se asume que los s atributos de S son también las últimas s columnas de R; por
tanto, los atributos que aparecen en el cociente son los (r − s) primeros de R. De ahı́ el formato
de las tres proyecciones (en realidad, la misma usada tres veces).
1
En realidad, la relación de orden entre los grados (r > s) no es simplemente una cuestión de tamaño, sino que
expresa un criterio aún más restrictivo. Ante todo, para que el cociente tenga algún sentido, es obvio que R y S
han de tener atributos comunes; pero ni siquiera esto es suficiente. Lo que se exige es que todos los atributos de
S sean comunes con R, es decir, que los atributos de S constituyan un subconjunto de los atributos de R. Esto
significa, básicamente, que no puede haber atributos en S que no estén en R y se “ignoren” al hacer la división.
Pero, no obstante, es fácil emular esta situación simplemente haciendo uso del operador de proyección (π).
2
Tuplas como éstas y las anteriores constituyen lo que hubiera sido “el resto” de este cociente. De hecho, el
cociente relacional se parece en este sentido a la división entera; y no es reversible precisamente debido a que en
Álgebra Relacional el resto se descarta, en lugar de ser sumado a posteriori.

2
Agrupando los atributos se puede obtener una expresión más clara. Ası́, se denominará B al
conjunto de los s atributos comunes a las dos relaciones (es decir, todos los de S), y A al resto
(es decir, los r − s atributos exclusivos de R). En tal caso, el esquema de relación del dividendo
tiene la forma R(A, B), el del divisor tiene la forma S(B), y por tanto el del cociente será T (A).
La expresión resultante tiene esta forma simplificada:

R(A, B) ÷ S(B) ≡ πA (R) − πA ((πA (R) × S) − R)

Esto significa lo siguiente: se toman las columnas A de R, es decir, todos los fragmentos de
tupla (πA (R)) que podrı́an aparecer en el resultado. El conjunto de estos fragmentos se multiplica
por la relación S, con lo que se obtienen todas las combinaciones posibles (πA (R) × S); lo que
incluye todas las tuplas de R, pero también algunas falsas. Por tanto, al restar a este conjunto
la relación R original, se obtiene la lista de todas las posibles tuplas espurias ((πA (R) × S) − R).
Al proyectar los atributos A de esta lista, se obtiene la relación de todos los fragmentos de tupla
que no pueden formar parte del cociente (πA ((πA (R) × S) − R)). Por lo tanto, al restar esta
relación del conjunto de los que podrı́an formar parte, se obtiene el conjunto de los que, en
efecto, están en el cociente.
Por supuesto, el orden de las columnas no importa; los atributos de A no tienen por qué ser
los primeros, ni los de B tienen por qué seguir la misma secuencia en las dos relaciones. Sin
embargo, suelen indicarse con el mismo orden para facilitar la legibilidad.
El significado intuitivo de esta operación es difı́cil de expresar; básicamente, extrae la infor-
mación que complementa a todo un conjunto de información que se tiene previamente.
Es mucho más fácil verlo cuando el grado de S es uno: en ese caso, el cociente expresa toda
la información que es común a todo un conjunto de objetos o conceptos dado. Ha-
bitualmente, se solicita mediante frases del tipo “indique el conjunto de elementos-A que tienen
la relación Z con todos los objetos B” 3 .
Un sencillo ejemplo de uso de esta operación es el siguiente:

R: A B C D S: C D R ÷ S: A B
1 2 3 5 3 5 1 2
4 3 5 9 2 7
3 2 8 1
1 2 2 7
1 3 2 7

Otro ejemplo, que muestra más claramente el objetivo de este operador (incluso actuando
sobre una única tabla), podrı́a ser el siguiente. Sea la tabla COCHES(Id, Modelo, Color), se
quiere obtener una lista de todos los modelos para los que hay coches de todos los colores. La
respuesta es (πM odelo, Color (Coches)÷πColor (Coches)), tal como se desarrolla a continuación para
un caso concreto.
3
Más adelante se revisa un ejemplo concreto de este tipo, en el que B es un conjunto de colores, y A una lista
de modelos de coches. La relación Z es el hecho de que exista un coche de cierto modelo y con cierto color.

3
Coches πM odelo, Color (Coches) πColor (Coches)
Id Modelo Color Modelo Color Color
C1 Jaguar Rojo Jaguar Rojo Rojo
C2 Jaguar Gris Jaguar Gris Gris
C3 Jaguar Verde Jaguar Verde Verde
C4 Jaguar Verde TransAm Rojo Azul
C5 TransAm Rojo TransAm Azul
C6 TransAm Azul Daimler Gris πM,C (Co) ÷ πC (Co)
C7 Daimler Gris Jaguar Azul Modelo
C9 Jaguar Azul TransAm Verde Jaguar
C10 TransAm Verde

Implementar este operador en un lenguaje de programación convencional no es sencillo en


absoluto; es fácil comprobarlo simplemente tratando de programar esta funcionalidad en C o
Pascal, por ejemplo. Incluso en un lenguaje especı́fico, como el propio Sql, la expresión no es
ni mucho menos obvia; véase por ejemplo una consulta equivalente al ejemplo previo.

SELECT Modelo
FROM Coches C1
WHERE NOT EXISTS (SELECT Color
FROM Coches C2
WHERE NOT EXISTS (SELECT ∗
FROM Coches C3
WHERE C1.Modelo = C3.Modelo AND
C2.Color = C3.Color))

Reunión (Join)
También es conocida como composición, pero habitualmente se menciona, sin embargo, mediante
el término en inglés. No añade ninguna potencia semántica al álgebra (en realidad persigue el
mismo objetivo que el producto cartesiano), pero permite mantener bajo control el tamaño de
las relaciones resultantes, que tiende a crecer rápidamente con toda operación-producto.
Sean dos relaciones R y S cualesquiera (de grados r y s, respectivamente), se define la
φ-reunión de estas relaciones sobre sus columnas (atributos) i y j, respectivamente (tales que
i ≤ r y j ≤ s), como el conjunto de todas las tuplas obtenidas combinando las tuplas de R y S,
cuando se cumple que la columna i-ésima (procedente de R) está en relación φ con la columna
j-ésima (procedente de S)4 .
La relación φ puede ser cualquiera, pero habitualmente se trata de la igualdad (=), que es
el caso arquetı́pico (mencionado a menudo en la bibliografı́a como equijoin), o de una relación
de orden (>, ≥, ≤, <), realizada habitualmente entre campos de tipo numérico o fecha.
4
Una vez que R y S se han reunido en la nueva relación, que tendrá grado r + s, y asumiendo que se mantiene
el orden de los atributos, poniendo en primer lugar los r de R, se puede decir que “la columna i-ésima está en
relación φ con la columna (r + j)-ésima” de la nueva relación. Se sigue esta misma idea en la posterior equivalencia
del operador derivado.

4
Se expresa mediante la sintaxis siguiente, y su traducción a los operadores básicos es inme-
diata, ya que se trata simplemente de una restricción sobre el producto cartesiano:

1 S ≡ σiφ(r+j) (R × S)
Riφj

Su función intuitiva es muy sencilla: reúne toda la información relativa a un tema dado,
considerando solamente aquélla que es a priori coherente con la pregunta (esto es, con la consulta
que se está intentando hacer), e ignorando el resto. Su intención es pues análoga a la del producto
cartesiano (×), ya que tiene como objetivo reunir información procedente de tablas diferentes
en un único punto (un único elemento del modelo, es decir, una tabla). De hecho, el producto
es la única forma de reunir información que de otra forma se encuentra dispersa; sin embargo,
tiene el problema de la explosión combinatoria: el tamaño de las tablas-producto tiende a crecer
demasiado rápidamente.
La importancia del join deriva justamente de esto: al indicar desde el principio cuál es la
condición que hará a ciertas tuplas interesantes, se controla el tamaño de las tablas resultantes,
ya que todas las combinaciones irrelevantes se descartan de inmediato. Por supuesto, si el
operador se implementase mediante su traducción a los básicos, el producto se realizarı́a de
todos modos; pero en una implementación real, una versión optimizada del join podrı́a evitar
la generación de información espuria.
El siguiente ejemplo muestra dos tablas con datos sobre actores y actrices; se pide el nombre
de las actrices con mayor edad que algún actor 5 . La expresión algebraica correspondiente a esta
pregunta, y un ejemplo de aplicación, se indican a continuación:

πIdentidad (ActoresEdad<Anyos
1 Actrices)

Actores Actrices 1
Actores Edad<Anyos Actrices
Nombre Edad Identidad Anyos Nombre Edad Identidad Anyos
G. Peck 80 S. Sarandon 50 K. Reeves 23 S. Sarandon 50
K. Reeves 23 N. Portman 18 T. Cruise 30 S. Sarandon 50
T. Cruise 30 J. Roberts 28 K. Reeves 23 J. Roberts 28

Es necesario relacionar dos tablas distintas, luego es necesario “multiplicarlas”, como en un


producto cartesiano. Sin embargo, puede verse como el tamaño de la relación resultante es
mucho más reducido que el del producto correspondiente, pues ya se han eliminado todas las
tuplas que no cumplen la condición. Todas las tuplas que quedan expresan la información “el
actor A, de edad X, es más joven que la actriz B, de edad Y”. Obsérvese que no sólo desaparecen
combinaciones absurdas, sino que incluso se elimina información, no relevante para la pregunta,
pero que de hecho estaba en las tablas originales, como el actor más viejo que todas las actrices
(G. Peck en el ejemplo), o la actriz más joven que todo actor (N. Portman en el ejemplo).
5
No debe confudirse con otra pregunta, mucho más compleja, referida a las “actrices con mayor edad que todos
los actores”, que necesitarı́a hacer uso del cociente. Una posible solución a ésta serı́a la siguiente:

πIdentidad ((ActricesEdad<Anyos
1 Actores) ÷ Actores)

5
En el ejemplo se han usado nombres diferentes para atributos similares en ambas relaciones,
con el fin de evitar cualquier confusión con el join natural. En realidad, el resultado hubiera sido
exactamente el mismo aun en el caso de que los atributos de ambas relaciones hubieran tenido el
mismo nombre (Nombre, Edad); pero en ese caso deberı́a usarse el operador de renombrado (ρ)
sobre atributos, ya que el modelo relacional prohı́be expresamente que una tabla tenga nombres
de columna duplicados. El operador no considera este caso, por lo que la responsabilidad de
controlarlo cae completamente en el diseñador de la consulta.
La ventaja del operador es mayor de la que parece: no sólo se tiene una notación más
compacta para una operación muy habitual, sino que el tamaño de los cálculos intermedios
puede reducirse en gran medida. Pero en realidad, y a pesar de todo lo que se ha dicho, ante
todo el join sólo es importante porque generaliza a una operación aún más frecuente: el join
natural, que es absolutamente fundamental en el álgebra relacional.

Reunión Natural (Join Natural)


Se puede definir de manera muy sencilla como un caso especial del φ-join, en el que la función φ
expresa una igualdad; pero es sin embargo el más importante de ambos. En realidad, es el que
justifica la definición de los dos, ya que expresa el acto más habitual en una consulta: “recorrer
un enlace” (una clave ajena) entre dos tablas.
Formalmente, es un join de igualdad (un equijoin) sobre los atributos homónimos de dos
relaciones. Es decir, dadas dos tablas cualesquiera R y S, se toman todos los atributos que tienen
el mismo nombre en ambas, y se hace un join, cuya condición es la igualdad entre los valores de
dichos atributos. El significado intuitivo es muy claro: dadas dos tablas con atributos comunes,
su join natural es la tabla creada mediante la fusión de las tuplas en las que estos atributos
comunes son iguales. Por supuesto, sólo permanece una copia de las columnas duplicadas.
El join natural se expresa con la sintaxis R 1 S; es decir, usa el mismo operador que la
anterior, sin necesidad de indicar ninguna condición φ especial. Su descripción en función de los
operadores básicos es casi inmediata:

R 1 S ≡ πi1 ,i2 ,...,im (σR.A1 =S.A1 ∧...R.Ak =S.Ak (R × S))

Grosso modo, basta con realizar el producto cartesiano (×), seleccionar (σ) las tuplas con los
atributos iguales, y proyectar (π) el resultado para dejar únicamente las columnas relevantes.
La descripción formal indicada tiene en realidad dos sutilezas: en primer lugar, se asume que los
atributos A1 , A2 , . . . , Ak son todos los atributos que tienen el mismo nombre tanto en R como
en S. De este modo, la selección (σ) elige solamente aquellos en los que los valores de todos ellos
son simultáneamente iguales.
Menos elegante es la forma de la proyección (π). Dado que la copia final ha de eliminar
duplicados, se asume que disponemos de un ı́ndice ordenado i1 , i2 , . . . , im , que incluye a todos
los atributos no comunes, y una única copia de cada uno de los comunes. De este modo, se
seleccionan las m columnas relevantes, y se descarta la segunda copia de las k comunes6 .
6
De lo que se sigue que m + k = r + s. En realidad, esto no es más que un artificio para simplificar la definición.
En la práctica, no se usa un ı́ndice {ij }j=1...m , sino que simplemente se descartan las k repetidas.

6
El objetivo del join natural es reunir toda la información acerca de un mismo objeto o
concepto, que estaba previamente dispersa en dos (o más) relaciones. Éste es el tipo de consulta
más normal, y sin embargo resulta complejo expresarla en términos de los operadores básicos.
De hecho, la única operación capaz de relacionar dos tablas distintas es el producto cartesiano,
que es tedioso y complejo, y genera un gran número de combinaciones innecesarias. Resulta
mucho más natural esta reunión basada en el contenido, que sólo maneja información relevante:
de ahı́ el nombre del operador. No obstante, sigue siendo un operador derivado.
Es un operador requerido por el propio modelo relacional, ya que se adapta perfectamente
a la idea de recorrer un enlace entre dos relaciones para realizar una consulta. En efecto,
resulta totalmente coherente con la definición de clave ajena como un conjunto de atributos de
la relación R1 cuyo contenido ha de coincidir con la clave primaria de la relación referenciada,
R2 . Al hacer un join natural entre dos tablas de esta forma, simplemente se seleccionan las
tuplas en las que la clave primaria de R2 coindice con la clave ajena de R1 , y se descartan los
duplicados. El resultado equivale a haber navegado por el enlace desde R1 hacia R2 .
Un ejemplo sencillo de join natural es el siguiente, que tiene como rasgo notable hacer la
fusión de dos columnas (atributos) homónimas, A y C, en lugar de solamente una, que es lo más
habitual cuando se trata de recorrer una clave ajena.
R: A B C D S: A C E R1S: A B C D E
1 3 5 7 1 5 2 1 3 5 7 2
3 2 9 1 1 5 9 1 3 5 7 9
2 3 5 4 3 9 2 3 2 9 1 2
2 3 7

Al igual que el producto cartesiano, con el que no deja de guardar una estrecha relación, el
join natural es conmutativo y asociativo: no resulta afectado, pues, por el orden en el que se
realicen las operaciones.