Pensar bien si hay que contar con distinct o sin él

Contar parece fácil. Pero hay que asegurarse de que no se cuentan valores repetidos, salvo que sea necesario. Este es un fallo frecuente. Lo estudiamos con unos sencillos ejemplos. 

Vamos a suponer que tenemos estas dos tablas, que contienen información de los amigos y enemigos de personas que son usuarios de una supuesta red social (se supone que los valores de los campos apodo, amigo y enemigo son valores que identifican a usuarios de la red social, es decir, serán valores de una clave primaria de una tabla de usuarios que no se muestra por simplicidad):

Amigos
+--------+---------+
| apodo  | amigo   |
+--------+---------+
| ana77  | aabad7  |
| ana77  | albabi5 |
| bea39  | bruno15 |
| ccruz5 | carla8i |
+--------+---------+
4 rows in set (0.00 sec)
Enemigos
+--------+----------+
| apodo  | enemigo  |
+--------+----------+
| ana77  | andres34 |
| bea39  | carla8i  |
| ccruz5 | berta8   |
| ccruz5 | carmen78 |
+--------+----------+
4 rows in set (0.00 sec)

Primero, supongamos que queremos contar cuantos amigos tiene cada persona. Lo hacemos con esta consulta que usa la función count sin distinct

BIEN
select apodo, count(amigo) 
from Amigos 
group by apodo;

que devuelve como resultado:

+--------+--------------+
| apodo  | count(amigo) |
+--------+--------------+
| ana77  |            2 |
| bea39  |            1 |
| ccruz5 |            1 |
+--------+--------------+
3 rows in set (0.00 sec)

En segundo lugar, vamos a contar, con una única consulta, cuantos amigos y enemigos tiene cada persona. Para ello realizamos esta consulta, también, y erróneamente, usando la función count sin distinct:

MAL
select A.apodo, count(amigo), count(enemigo)
from Amigos A natural join Enemigos
group by A.apodo;

Ahora el resultado es el siguiente:

+--------+--------------+----------------+
| apodo  | count(amigo) | count(enemigo) |
+--------+--------------+----------------+
| ana77  |            2 |              2 |
| bea39  |            1 |              1 |
| ccruz5 |            2 |              2 |
+--------+--------------+----------------+
3 rows in set (0.00 sec)

Es obvio que no es correcto: realmente ana77 tiene dos amigas y un enemigo, y ccruz5 tiene un amigo y dos enemigos. ¿Por qué sale mal? Veamos el resultado de la consulta que sirve de base para el contaje:

select A.apodo, amigo, enemigo
from Amigos A natural join Enemigos;
+--------+---------+----------+
| apodo  | amigo   | enemigo  |
+--------+---------+----------+
| ana77  | albabi5 | andres34 |
| ana77  | aabad7  | andres34 |
| bea39  | bruno15 | carla8i  |
| ccruz5 | carla8i | berta8   |
| ccruz5 | carla8i | carmen78 |
+--------+---------+----------+
5 rows in set (0.00 sec)

Si nos fijamos en el grupo de ana77 podemos entender que no es lo mismo contar sus enemigos (count(enemigo)), que contar sus diferentes enemigos (count(distinct enemigo)). Usar count(enemigo) devolverá 2 porque hay dos valores (aunque sean el mismo, están en filas diferentes, por lo que se cuentan ambos), mientras que count(distinct enemigo) devolverá 1 porque distinct dentro de count hace que no se cuenten valores repetidos.

En el ejemplo que estamos estudiando es necesario contar diferentes amigos/enemigos porque al hacer el join de ambas tablas, cada fila de la tabla de amigos se enlaza con todas las de la tabla enemigos con mismo apodo. Esto puede provocar repeticiones (como puede verse en el resultado anterior).

Así que la solución correcta es la siguiente:

BIEN
select A.apodo, count(distinct amigo), count(distinct enemigo)
from Amigos A natural join Enemigos
group by A.apodo;

Que arroja como resultado:

+--------+-----------------------+-------------------------+
| apodo  | count(distinct amigo) | count(distinct enemigo) |
+--------+-----------------------+-------------------------+
| ana77  |                     2 |                       1 |
| bea39  |                     1 |                       1 |
| ccruz5 |                     1 |                       2 |
+--------+-----------------------+-------------------------+
3 rows in set (0.00 sec)

Como conclusión: en un caso hemos contado con distinct y en otro no. Hay que estudiar bien la tabla sobre la que se hace el contaje para detectar la posibilidad de existencia de valores repetidos que harían necesario usar distinct.

No hay comentarios:

Publicar un comentario