Sunteți pe pagina 1din 16

Consultas y unión de tablas

Select y Operaciones
Utilizando SELECT podemos realizar operaciones y obtener los resultados como columna
temporales.

Por ejemplo existe la función CONCAT que permite unir dos o más strings. Entonces podemos
escribir:

Select CONCAT(nombre, apellido) FROM directorio_telefonico;

Veremos:

concat
-----------
JuanPerez
(1 row)

Podemos agregar un espacio para hacer los resultados más legibles.

SELECT CONCAT(nombre, ' ', apellido) FROM directorio_telefonico;

concat
-----------
Juan Perez
(1 row)

Podemos renombar la columna del resultado ocupando la instrucción AS que aprendimos:

SELECT CONCAT(nombre, ' ', apellido) AS nombre_completo FROM


directorio_telefonico;
Veamos otro ejemplo: Generemos una consulta sobre el largo de cada nombre en nuestra tabla de
directorio telefónico:

-- seleccionaremos el largo de los nombres


SELECT LENGTH(nombre)
-- y le asignaremos el nombre largo_de_nombre
AS largo_del_nombre
-- de la tabla Directorio_telefonico
FROM Directorio_telefonico

largo_del_nombre

7
Otras operaciones
Otras operaciones frecuentemente utilizadas son:

CONCAT : Une dos o más strings.


LENGTH : Calcula el largo de los datos en una columna.
COUNT : Cuenta la cantidad de registros.
DISTINCT : Selecciona solo datos distintos.
MIN : Entrega el mínimo de los datos de una columna.
MAX : Entrega el máximo de los datos de una columna.
SUM : Entrega la suma de los datos de una columna.
AVG : Entrega el promedio de datos de una columna.

Por ejemplo podemos utilizar para calcular la edad promedio de nuestro contactos.

SELECT AVG(edad) FROM directorio_telefonico;

Hace poco aprendimos a seleccionar el mayor de los valores combinando las instrucciones de order
by y limit pero también dijimos que había una forma más fácil de hacerlo, y es con la función MAX

SELECT MAX(edad) FROM Directorio_telefonico;

Obtendremos como resultado:

max
-----
21
(1 row)

Sin embargo si queremos seleccionar otras columnas en conjunto con el máximo para saber a quien
corresponde obtendremos un error.

SELECT *, MAX(edad) FROM Directorio_telefonico;

ERROR: column "directorio_telefonico.nombre" must appear in the GROUP BY


clause or be used in an aggregate function
LINE 1: SELECT *, MAX(edad) FROM Directorio_telefonico;

MAX, COUNT, MIN y SUM son operaciones que se realizan sobre agrupaciones de datos y para
utilizarlas correctamente todavía nos falta aprender a agrupar.
Distintos
Podemos seleccionar datos distintos, o sea eliminar los repetidos, utilizando la función DISTINCT .

Probemos comparando una selección sin distinct y otra con la función.

SELECT edad FROM directorio_telefonico;

SELECT DISTINCT(edad) FROM directorio_telefonico;

Luego podemos ordenar los resultados utilizando ORDER BY

SELECT DISTINCT(edad) FROM directorio_telefonico ORDER BY edad;

Aviso: Al seleccionar múltiples campos se evaluarán las combinaciones donde todos sean únicos.

SELECT DISTINCT(edad), nombre FROM directorio_telefonico ORDER BY edad;

Como en nuestros datos todos los usuarios tienen distintos nombres no hay combinaciones repetidas,
por lo tanto se muestran todos los datos.
Agrupaciones
SQL nos permite agrupar datos en base a un criterio, por ejemplo agrupando también podemos
seleccionar todos los usuarios que tienen edades distintas.

SELECT edad FROM directorio_telefonico GROUP by edad;

La ventaja es que al agrupar podemos aplicar otras funciones, por ejemplo podemos contar.

SELECT COUNT(edad) FROM directorio_telefonico GROUP by edad;

count
-------
1
1
4
2
3

Para mostrar a que edad está asociado a cada cuenta podemos seleccionar ambas columnas.

edad | count
------+-------
19 | 1
23 | 1
20 | 4
22 | 2
21 | 3

Sin embargo cuando agrupamos datos no podemos seleccionar las columnas sobre los datos
agrupados.

SELECT nombre, edad, count(edad) FROM directorio_telefonico GROUP by edad;

ERROR: column "directorio_telefonico.nombre" must appear in the GROUP BY


clause or be used in an aggregate function
LINE 1: SELECT nombre, edad, count(edad) FROM directorio_telefonico

El aprendizaje importante es que si agrupamos tenemos que trabajar con las columnas agrupadas.
Trabajando con múltiples tablas
En algunas situaciones necesitaremos consultar datos cuya información está repartida en mas de una
tabla.

Veamos el siguiente caso donde tenemos una tabla de artículos (posts) y otra de comentarios
(comments). Los posts tendrán un título, contenido y un identificador numérico para asegurarnos de
que sean únicos. Los comentarios tendrán solamente contenido y una referencia el identificador del
artículo para saber a que artículo pertenecen.

CREATE TABLE posts(


id SERIAL,
title VARCHAR,
content TEXT
);

INSERT INTO posts (title, content) VALUES ('Artículo 1', 'LOREM IPSUM ET ...
');
INSERT INTO posts (title, content) VALUES ('Artículo 2', 'LOREM IPSUM ET ...
');

Los identificadores se asignarán automáticamente de forma incremental, el primer artículo que


ingresemos tendrá id 1, y el siguiente 2. Si borramos estos artículos para ingresar nuevos el número
seguirá incrementando. Es posible resetear el contador pero no es algo relevante en este momento.

id title content

1 Artículo 1 LOREM IPSUM ET ...

2 Artículo 2 LOREM IPSUM ET ...

Ahora ingresaremos los comentarios.

CREATE TABLE comments(


content text,
post_id integer
);

INSERT INTO comments (content, post_id) VALUES ('comentario 1', 1);


INSERT INTO comments (content, post_id) VALUES ('comentario 2', 1);
INSERT INTO comments (content, post_id) VALUES ('comentario 1', 2);

content post_id

comentario 1 1

comentario 2 1

comentario 1 2
¿Cómo podemos obtener todos los comentarios indicando a que artículo pertenecen?

La respuesta utilizando un select pero ahora donde utilizamos el from utilizaremos ambas tablas.

SELECT * FROM comments, posts;

content post_id id title content

comentario 1 1 1 Artículo 1 LOREM IPSUM ET ...

comentario 1 1 2 Artículo 2 LOREM IPSUM ET ...

comentario 2 1 1 Artículo 1 LOREM IPSUM ET ...

comentario 2 1 2 Artículo 2 LOREM IPSUM ET ...

comentario 1 2 1 Artículo 1 LOREM IPSUM ET ...

comentario 1 2 2 Artículo 2 LOREM IPSUM ET ...

Pero esto no muestra exactamente la información que queremos, porque cada comentario aparece
asociado a ambos artículos y eso es un error. La solución es sencilla tenemos que especificar en la
consulta que cada comentario tiene que estar asociado a su respectivo artículo.

SELECT * FROM comments, posts WHERE post_id = id;

content post_id id title content

comentario 1 1 1 Artículo 1 LOREM IPSUM ET ...

comentario 2 1 1 Artículo 1 LOREM IPSUM ET ...

comentario 1 2 2 Artículo 2 LOREM IPSUM ET ...

Lidiando con los conflictos de nombre


Sucederá seguido que tendremos dos tablas con una columna con el mismo nombre, por ejemplo
muchas tablas usan el nombre id para guardar un identificador principal, por eso cuando realizamos
un select y nos referimos a una columna a lo largo de múltiples tablas podemos ocupar la notación de
tabla.columna.

Entonces si nuestra tabla de comentarios hubiese tenido la columna id habríamos tenido que escribir
nuestra consulta de la siguiente forma:

SELECT * FROM comments, posts WHERE post_id = posts.id;


Joins (uniones)
Existe una forma mucho mas flexible de unir elementos de ambas tablas, este es es utilizando
uniones o joins

SELECT columnas FROM tabla1 JOIN tabla2 ON tabla1.columna=tabla2.columa;

Para recordarlo tomemos en cuenta lo siguiente:

-- Seleccionamos las columnas desde la tabla1


SELECT columnas FROM tabla1
-- Posterior a la selección de la columna, indicamos que vamos a generar la
unión
-- con la columna de la tabla2
JOIN tabla2 ON tabla1.columna=tabla2.columa;

Con lo aprendido podemos reescribir nuestra consulta para mostrar todos los artículos junto con los
comentarios.

SELECT * FROM comments JOIN posts on posts.id = comments.post_id;

Dijimos que los uniones (joins) son mas flexibles pero para demostrarlo vamos a ingresar un datos
mas a la tabla de comentarios sin ningún post asociado.

INSERT INTO comments (content) VALUES ('comentario no asociado');

Si repetimos nuestra consulta veremos que el resultado no ha cambiado.

SELECT * FROM comments JOIN posts on posts.id = comments.post_id;

content post_id id title content

comentario 1 1 1 Artículo 1 LOREM IPSUM ET ...

comentario 2 1 1 Artículo 1 LOREM IPSUM ET ...

comentario 1 2 2 Artículo 2 LOREM IPSUM ET ...

(3 rows)

Esto sucede por que la unión solo nos devuelve los elementos donde hay una relación a menos que
lo especifiquemos, podemos especificar otros tipos de joins con el modificar LEFT o RIGHT
SELECT * FROM comments LEFT JOIN posts on posts.id = comments.post_id;

content post_id id title content

comentario 1 1 1 Artículo 1 LOREM IPSUM ET ...

comentario 2 1 1 Artículo 1 LOREM IPSUM ET ...

comentario 1 2 2 Artículo 2 LOREM IPSUM ET ...

comentario no asociado

(4 rows)
Tipos de join
INNER JOIN: Une sólo las columnas comunes entre ambas tablas, inner es implícito

LEFT JOIN: Une todas las columnas de la primera tabla con las columnas en común de la segunda
tabla

FULL OUTER JOIN: Une todas las columnas de ambas las tablas
Integridad referencial
La integridad referencial es un concepto muy importante puesto que nos ayuda a evitar que nuestros
datos se corrompan. ¿Cómo pueden corromperse?

Volvamos a un concepto previo muy importante que son las reglas del negocio. Estas reglas de
negocio no son universales, dependen de las necesidades de distintos proyectos. Por ejemplo si
tenemos una aplicación que consiste en un blog, donde tenemos artículos y comentarios puede hacer
sentido que evitemos que un comentario quede en el aire y que no le pertenezca a ningún artículo.
Imponer este arregla ayudaría a evitar que cuando alguien borre un artículo el comentario quede
flotando o ensuciando la base de datos y completamente inaccesible.

En términos técnicos la integridad referencial nos asegura que la clave externa a la que se hace
referencia es válida.

Podemos implementar esta regla agregando una clave foránea, para hacer esto tenemos que
modificar nuestra tabla pero también es posible agregarla la momento de crearla.

Para modificar una tabla y agregar la clave foránea la sintaxis es la siguiente:

ALTER TABLE child_table


ADD CONSTRAINT constraint_name FOREIGN KEY (c1) REFERENCES parent_table (p1);

Si lo aplicamos a nuestro ejemplo sería:

ALTER TABLE comments


ADD CONSTRAINT fk_comments_post FOREIGN KEY (post_id) REFERENCES posts (id);

Pero obtendremos el error:

INT fk_comments_post FOREIGN KEY (post_id) REFERENCES posts (id);


ERROR: there is no unique constraint matching given keys for referenced table
"posts"

Esto se debe a que el id de los posts no es único y si no es único no hay un artículo único al que
estará asociado el comentario lo que es una violación a la integridad referencial.

Las claves foráneas deben estar asociadas a claves ,primarias, de esta forma nos aseguramos que
los valores sean únicos y no puedan ser nulos.

ALTER TABLE posts


ADD PRIMARY KEY (id);
Y luego podemos aplicar nuestra restricción de clave foránea sin errores.

ALTER TABLE comments


ADD CONSTRAINT fk_comments_post FOREIGN KEY (post_id) REFERENCES posts (id);

Podemos ver el constraint si escribimos \d

Table "public.comments"
Column | Type | Modifiers
---------+---------+-----------
content | text |
post_id | integer |
Foreign-key constraints:
"fk_comments_post" FOREIGN KEY (post_id) REFERENCES posts(id)

Aprovechemos la oportunidad de poner a prueba la integridad referencial, para hacer esto vamos a
modificar uno de los comentarios y asignaremos un post_id de un post que no existe.

UPDATE comments SET post_id=100 WHERE post_id=2;

Y obtendremos un error:

ERROR: insert or update on table "comments" violates foreign key constraint


"fk_comments_post"
DETAIL: Key (post_id)=(100) is not present in table "posts".

Ese es un error bueno, en el sentido de que evita que programas o usuarios que hagan uso de la
base de datos ingresen valores inválidos.

Estas restricción también evita que se borra un post que tenga comentarios.

DELETE FROM post WHERE id=1;

Obtendremos el mismo error:

ERROR: update or delete on table "posts" violates foreign key constraint


"fk_comments_post" on table "comments"
DETAIL: Key (id)=(1) is still referenced from table "comments".

También es posible agregar una regla de borrado en cascada, revisarlo en la documentación de


PostgreSQL
Uniones y agrupaciones
Un tipo de consulta muy común es utilizar operaciones de agrupación, por ejemplo consultar cuantos
comentarios tiene cada artículo. Para obtener la consulta necesitamos unir los posts y los
comentarios, agrupar los comentarios asociados a cada posts y contarlos.

SELECT posts.*, count(comments) as comments_count


FROM posts
LEFT JOIN comments on comments.post_id = posts.id
GROUP BY (posts.id);

id title content count

1 Artículo 1 LOREM IPSUM ET ... 2

2 Artículo 2 LOREM IPSUM ET ... 1

3 Artículo 3 LOREM IPSUM ET ... 0

Aquí podemos ver otro uso importante del LEFT JOIN, si en su lugar hubiésemos ocupado
simplemente un JOIN el artículo 3 no hubiese sido mostrado pues el inner join (o simplemente join)
solo muestra resultados cuando existen elementos asociados.

Filtrando sobre operaciones agrupadas


Con SELECT y WHERE podemos filtrar los resultados que cumplen ciertas condiciones, por ejemplo
podríamos pedir en específico un artículo o obtener todos los artículos que contengan cierto
contenido pero en algunos casos necesitamos filtrar sobre las operaciones que hicimos en nuestros
datos agrupados. Por ejemplo supongamos que necesitamos para nuestro reporte mostrar solo los
resultados cuyo cantidad de comentarios sea mayor o igual que dos, para eso utilizaremos HAVING

SELECT posts.*, count(comments) as comments_count


FROM posts
LEFT JOIN comments on comments.post_id = posts.id
GROUP BY (posts.id)
HAVING count(comments) >= 2;

id title content comments_count

1 Artículo 1 LOREM IPSUM ET ... 2

Este tipo de operaciones son muy útiles a la hora de generar reportes.


Sub-consultas (subqueries)
En algunas situaciones es necesario dividir nuestra consulta en dos partes, una consulta inicial, la
que llamaremos consulta interna, obtendrá ciertos resultados y estos serán utilizados por la segunda
consulta, la que llamaremos consulta externa. Las consultas pueden ser ambas a la misma tabla o
tabla distintas.

Veamos un caso donde podría ser útil, supongamos que necesitamos seleccionar todos los usuarios
de nuestro directorio telefónico que cumplan que su edad sea mayor o igual que el promedio. De esta
forma necesitamos una consulta interna para seleccionar el promedio y otra externa para seleccionar
a los usuarios.

Para resolver el problema el primer paso es identificar la consulta interna, en este caso encontrar la
edad promedio.

SELECT AVG(edad) FROM directorio_telefonico

Ahora solo necesitamos que las edades de nuestros usuarios sean mayores a este número y eso lo
podemos lograr con un simple WHERE

SELECT * from directorio_telefonico WHERE


edad >= (SELECT AVG(edad) FROM directorio_telefonico);

*[Nota] Las consultas internas deben estar encapsuladas entre paréntesis.

También podemos utilizar información de distintas tablas, por ejemplo si necesitáramos los
comentarios de los últimos 5 posts. Y aquí claramente hay dos consultas primero necesitamos
obtener los ids de los últimos 5 artículos y luego seleccionar los comentarios cuyo post_id esté entre
estos.

Obteniendo los últimos 5 artículos:

SELECT id FROM posts ORDER BY id DESC LIMIT 5;

Para seleccionar elementos que estén dentro de una tabla como la que acabamos de obtener
ocuparemos el operador IN

SELECT * FROM COMMENTS


WHERE post_id IN (SELECT id from posts order by id DESC LIMIT 5);

S-ar putea să vă placă și