Q&A: PostgreSQL: LEFT JOIN por tipo de registro

Esta publicación corresponde a una respuesta que he dado via chat en el grupo de ayuda PosgreSQL en Español en Telegram.

Un usuario tiene una tabla con dos columnas: tipo_objeto e id_objeto. Los objetos se almacenan en otras tablas, pero la tabla en la cual se almacena el objeto depende del tipo de objeto. En algunos contextos esto se denomina relaciones polimórficas.

Por ejemplo, se puede tener una tabla de recursos en la cual se almacena una lista de recursos por departamento. Los recursos pueden ser humanos o materiales. Tendríamos la tabla humanosmaterialesrecursos. Lo interesante es que en la de recursos tendríamos los campos siguientes: fk_depto_id, tipo_recurso, recurso_id. Este último campo es un identificador que se encuentra en la tabla humanos o materiales según lo que indique tipo_recurso.

¿Cómo hacer un LEFT JOIN entre la tabla que almacena la relación y las tablas con los objetos?

La respuesta es simple:

Se hace el LEFT JOIN a todas las tablas pero en la cláusula ON se agrega una condición AND filtrando por el tipo de objeto.

Esto se muestra en el siguiente ejemplo. Nota: el uso de un tipo enumeración es sólo para facilitar la distinción de cuál es el campo que define el tipo de objeto.

CREATE TYPE tipo AS ENUM ('entero', 'cadenas');

CREATE TABLE lista (id SERIAL, tipo tipo, objid INTEGER);
CREATE TABLE enteros (objid SERIAL, valor INTEGER);
CREATE TABLE cadenas (objid SERIAL, valor TEXT);

INSERT INTO enteros(objid, valor) VALUES (1, 1);
INSERT INTO enteros(objid, valor) VALUES (2, 2);

INSERT INTO cadenas(objid, valor) VALUES (1, 'hola');
INSERT INTO cadenas(objid, valor) VALUES (2, 'mundo');

INSERT INTO lista(tipo, objid) VALUES ('entero', 1);
INSERT INTO lista(tipo, objid) VALUES ('cadenas', 1);
INSERT INTO lista(tipo, objid) VALUES ('cadenas', 2);

SELECT lista.id, lista.tipo, lista.objid, enteros.valor AS enteros, cadenas.valor AS cadenas 
FROM lista
  LEFT JOIN enteros
    ON lista.objid = enteros.objid AND lista.tipo = 'entero'
  LEFT JOIN cadenas
    ON lista.objid = cadenas.objid AND lista.tipo = 'cadenas';

Resultado:
 id |  tipo   | objid | enteros | cadenas 
----+---------+-------+---------+---------
  1 | entero  |     1 |       1 | 
  2 | cadenas |     1 |         | hola
  3 | cadenas |     2 |         | mundo
(3 rows)

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *