Tags: group by
Encontrar duplicados
By admin on Mar 3, 2009 | In Aprendiz | 4 feedbacks »
Un requisito muy común cuando se trabaja con datos, es encontrar entradas duplicadas. El lenguaje SQL es muy potente en hacerlo, pero mucha gente prefiere utilizar Excel para encontrar duplicados. Mi opinión es que esta es buena para los pequeños conjuntos de datos (menos de 10.000 filas). Puedes utilizar el formato condicional (por ejemplo, con este: =CONTAR.SI(a:A,a2) > 1)), o
puedes utilizar el Filtro/avanzado menú de opciones para filtrar y copiar a otro lugar (hay que activar la casilla para copiar sólo registros únicos). Más modernas versiones de Excel tienen ahora un comando en el menú de Datos: "quitar duplicados"
En MySQL (y, de hecho, otras versiones de SQL) la forma más común de encontrar duplicados de las entradas de campo es el uso de la cláusula GROUP BY. Por ejemplo, para devolver las entradas para el campo "job" de la tabla emp que tienen más de 1 fila/empleado registrado contra ese "job" en particular:
Code:
select job, count(*) | |
-> from emp | |
-> group by job | |
-> having count(*) > 1; |
Si querías devolver registros registró más de 3 veces en la tabla solo necesitas modificar la cláusula HAVING.
Code:
select job, count(*) | |
-> from emp | |
-> group by job | |
-> having count(*) > 3; |
Por cierto, si sólo querías devolver distinta campo(s) en una tabla, puedes utilizar GROUP BY sin realizar suma. Ahora esto es todo muy bien, pero suponiendo que tienes la obligación de devolver todos los campos en un registro se produce cuando el identificador/campo es un duplicado. Podemos hacer esto por un subconsulta de IN:
Code:
mysql> select a.* from emp a | |
-> where a.job in | |
-> (select b.job | |
-> from emp b | |
-> group by b.job having count(*) >1); |
Todo muy bien hasta ahora, pero vamos a introducir una tabla segunda. Supongamos que queremos seleccionar los registros que se producen dos veces en las dos tablas. Podemos hacerlo utilizando el operador UNION para devolver todos los registros de ambas tablas. (recuerde usar UNION ALL, si no los duplicados sería reprimida). Entonces pongamos esto en un "inline view" y, a continuación, utilizar la cláusula GROUP BY para seleccionar sólo duplicados
Code:
select empno, ename, job | |
from | |
(select empno, ename, job | |
from emp | |
UNION ALL | |
select empno, ename, job | |
from emp_temp) as empt | |
group by empno, ename, job having count(*) > 1; |
No estoy seguro de cómo se enfoque en Excel! Aunque es bastante fácil en Mysql!
Diviértete con Mysql!
=====================================
Olivier [Visitor]
Hola!
Excelente artículo la verdad. Y felicidades por el blog.
Estoy interesado en el primer ejemplo. Sabrías cómo eliminar los duplicados encontrados? Es decir, eliminar todos los duplicados encontrados excepto uno. Si se han encontrado 3 filas con un valor igual y 5 con otro valor igual pues eliminar un total de 6 respectivas filas y dejar las 2 originales.
¿Seria muy complicado en mysql? Imagino que a mucha gente lo que le interesa con los duplicados es eliminarlos :-)
Un saludo.
--
Hola Olivier! Gracias por tu pregunta. Me costo un poco en pensar sobre esto.
Es possible eliminar los duplicados bastante facil, con condicion de que haya Primary Key.
Suponer que tenemos estas filas (en la table dup_take)
+-----+-------+----+
| sno | cno | id |
+-----+-------+----+
| 1 | CS112 | 1 |
| 1 | CS113 | 2 |
| 1 | CS114 | 3 |
| 2 | CS114 | 12 |
| 2 | CS112 | 4 |
| 3 | CS112 | 5 |
| 3 | CS114 | 6 |
| 4 | CS113 | 8 |
| 4 | CS112 | 7 |
| 5 | CS113 | 9 |
| 6 | CS113 | 10 |
| 6 | CS114 | 11 |
+-----+-------+----+
Queremos quedar con una fila de cada sno. Bueno, primero hagamos un "temporary table":
create temporary table to_delete (sno tinyint, cno varchar(5), min_id smallint);
Luego podemos insertar estos:
insert into to_delete (sno, min_id)
select sno, MIN(id) from dup_take group by sno having count(*) > 1;
Entonces podemos usar la palabra clave EXISTS (funciona como una prueba de existencia). Es decir, solo eliminar los registros donde "EXISTS" mas de una fila y donde el id no es el original:
delete from dup_take
where exists (
select * from to_delete
where to_delete.sno = dup_take.sno
and to_delete.min_id <> dup_take.id);
Por fin tenemos:
+-----+-------+----+
| sno | cno | id |
+-----+-------+----+
| 1 | CS112 | 1 |
| 2 | CS112 | 4 |
| 3 | CS112 | 5 |
| 4 | CS112 | 7 |
| 5 | CS113 | 9 |
| 6 | CS113 | 10 |
+-----+-------+----+
Saludos
Mark
Consultas con GROUP BY
By admin on Nov 3, 2008 | In Aprendiz | Send feedback »
¡Hola, no estoy hibernando todavía!¡He estado muy ocupado con el SqlServer en mi trabajo. Una cláusula útil que utilizo allí es "TOP" para limitar las filas que obtiene tu consulta. Por supuesto tenemos un equivalente en Mysql, es decir LIMIT. Esto es muy útil para establecer un orden en las filas de tu consulta; a proposito, además de restringir la pregunta a las primeras filas, puedes utilizar el OFFSET para saltar filas. Por ejemplo si quiero traer detrás 5 filas de la tabla EMP, pero no los 5 valores más bajos, podría utilizar la declaración siguiente
Code:
select sal from emp order by sal limit 5 offset 5; | |
+------+ | |
| sal | | |
+------+ | |
| 1250 | | |
| 1300 | | |
| 1500 | | |
| 1600 | | |
| 2450 | | |
+------+ | |
5 rows in set (0.01 sec) |
Ahora otro punto útil sobre la cláusula del LIMIT es se puede utilizarla en consultas de GROUP BY. Claro que tenga sentido, es que la cláusula del LIMIT está trabajando en las filas de registro que se vuelven para la exhibición. Puede ser que los conjuntos de datos grandes produzcan más filas que estás interesado adentro. Usando la cláusula de LIMIT restringirá el número de filas que buscas:
Code:
mysql> select deptno, sum(sal) from emp | |
-> group by deptno | |
-> order by sum(sal) desc | |
-> limit 2; | |
+--------+----------+ | |
| deptno | sum(sal) | | |
+--------+----------+ | |
| 20 | 10875 | | |
| 30 | 10650 | | |
+--------+----------+ | |
2 rows in set (0.00 sec) |
Pienso qué la combinación de GROUP BY con otras funciones y cláusulas, es lo que haga muy poderoso el sql en analizar bases de datos. El código siguiente demuestra esto, pues confía en el uso del operador de la UNION de exhibir diversos resúmenes en el mismo informe. (recuerde utilizar el mismo número de columnas y de mismo tipo de datos en la columna adentro a cada uno la declaración selecta):
Code:
mysql> select deptno, job, | |
-> 'TOTAL BY DEPT AND JOB' as category, | |
-> sum(sal) as sal | |
-> from emp | |
-> group by deptno, job | |
-> UNION ALL | |
-> select deptno, null, | |
-> 'TOTAL BY DEPT', sum(sal) | |
-> from emp | |
-> group by deptno | |
-> UNION ALL | |
-> select null, null, | |
-> 'GRAND TOTAL FOR TABLE', sum(sal) | |
-> from emp; | |
+--------+-----------+-----------------------+-------+ | |
| deptno | job | category | sal | | |
+--------+-----------+-----------------------+-------+ | |
| 10 | CLERK | TOTAL BY DEPT AND JOB | 1300 | | |
| 10 | MANAGER | TOTAL BY DEPT AND JOB | 2450 | | |
| 10 | PRESIDENT | TOTAL BY DEPT AND JOB | 5000 | | |
| 20 | ANALYST | TOTAL BY DEPT AND JOB | 6000 | | |
| 20 | CLERK | TOTAL BY DEPT AND JOB | 1900 | | |
| 20 | MANAGER | TOTAL BY DEPT AND JOB | 2975 | | |
| 30 | CLERK | TOTAL BY DEPT AND JOB | 950 | | |
| 30 | MANAGER | TOTAL BY DEPT AND JOB | 2850 | | |
| 30 | SALESMAN | TOTAL BY DEPT AND JOB | 6850 | | |
| 10 | NULL | TOTAL BY DEPT | 8750 | | |
| 20 | NULL | TOTAL BY DEPT | 10875 | | |
| 30 | NULL | TOTAL BY DEPT | 10650 | | |
| NULL | NULL | GRAND TOTAL FOR TABLE | 30275 | | |
+--------+-----------+-----------------------+-------+ | |
13 rows in set (0.54 sec) |
Mola mucho no? (je je !).
Antes de que me vaya, tengo que admitir a conseguir la mayor parte de a mis ejemplos del código este vez del libro SQL CookBook, publicado por O' Reilly. Puede encontrar el libro en este sitio Oreilly Puedo recomendar este libro totalmente.
Bueno, es la hora de irme pero espero que visites mi sitio otra vez pronto. Suerte con el trabajo. ¡Hasta la proxima!
Código hermoso
By admin on Sep 21, 2008 | In Aprendiz, Trucos avanzado | Send feedback »
¡Que me disculpen! - Un aplazamiento desde mi ultima entrada pasada. He comenzado un nuevo trabajo que utiliza SQLServer (¡que pena!), así que mi uso de Mysql ha sido muy pequeño últimamente.
Bueno, tomemos a un poco la mirada en la cláusula Group By. Una de las maneras mas comunes que lo utilizo es traer detrás los registros que se duplican más de una vez. Por ejemplo:
Code:
mysql> select ename, job, deptno,count(*) | |
from emp | |
group by ename, job, deptno | |
having count(*) > 1 | |
| |
; | |
+-------+----------+--------+----------+ | |
| ename | job | deptno | count(*) | | |
+-------+----------+--------+----------+ | |
| WARD | SALESMAN | 30 | 2 | | |
+-------+----------+--------+----------+ | |
1 row in set (0.00 sec) |
¿Ahora cómo obtendras (suprimir o actualización) el último parte insertado de este registro duplicado? En este caso hay 2 filas, uno con el empno de 7521, el último fila tiene el empno 8000. El código siguiente (en mi opinión humilde) es muy elegante, y es quizás mi pedacito preferido del sql que he visto...gracias a este libro . libro mysql
Code:
mysql> select * from emp | |
-> where empno not in (select min(empno) | |
-> from emp | |
-> group by ename, job, deptno); | |
| |
+-------+-------+----------+------+------------+------+------+--------+ | |
| empno | ename | job | mgr | hiredate | sal | comm | deptno | | |
+-------+-------+----------+------+------------+------+------+--------+ | |
| 8000 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250 | 500 | 30 | | |
+-------+-------+----------+------+------------+------+------+--------+ | |
1 row in set (0.01 sec) |
Aquí está otra manera de usar la cláusula del Group By (otra vez usando subconsultas) para traer detrás a los empleados con el sueldo más alto y más bajo del grupo de datos.
Code:
mysql> select ename, job, deptno | |
-> from emp | |
-> where sal in ((select min(sal) from emp), | |
-> (select max(sal) from emp)); | |
| |
+-------+-----------+--------+ | |
| ename | job | deptno | | |
+-------+-----------+--------+ | |
| SMITH | CLERK | 20 | | |
| KING | PRESIDENT | 10 | | |
+-------+-----------+--------+ | |
2 rows in set (0.10 sec) |
Ahora que, a mí está también el código hermoso. ¡Disfruta de tu codificación - hasta la próxima!