jueves, agosto 31, 2006

La funcion DISTINCT (PostgreSQL/SQL Server)

Bueno la historia comenzo cuando en una base de datos de SQL Server se necesitaban eliminar de una tabla unos datos duplicados, aunque soy nuevo en la oficina donde laboro dije que podia asumir el reto, pero naaa no pude hacerlo, tratando de mirar las posibles soluciones para almenos sacar la informacion que se necesitaba, recorde que en PostgreSQL existe una funcion llamada DISTINCT ON la cual tambien existe en SQL Server pero el comportamiento de esta al agregar varios campos no daba el resultado que necesitaba, es decir era lo mismo utilizar un DISTINCT que un GROUP BY, entonces con mas calma decide hacer pruebas en mi equipo con Postgres para saber la forma de hacerlo. Realizando las pruebas lo primero que note es que no es lo mismo DISTINCT a DISTINCT ON, el DISTINCT ON si retorna solo una fila unica descartando las demas y con esta funcion si tenia los resultados esperados pero esta no existia en SQL Server y tomando en cuenta que en el manual de PostgreSQL indicaban que la funcion DISTINCT ON podia retornar resultados inesperados entonces segui en busca de encontrar una solucion para ambos motores de base de datos. Ya habia intentado de varias formas obtener el resultado con subquery y el group by pero sin buenos resultados, Googleando un poco encontre la siguiente pagina donde ya alguien habia tenido el mismo inconveniente pero indico un sql donde habia obtenido los mismo resultados, observando el sql me di cuenta que se utilizaba la funcion MIN() entonces decidi realizar las pruebas:
--con distinct
SELECT DISTINCT ON (campo1) * FROM prueba2;
--sin el distinct to
SELECT b.* FROM ( SELECT campo1, min(campo6) as campo6 FROM prueba2 GROUP BY campo1 ) a, prueba2 b WHERE a.campo1=b.campo1 AND a.campo6=b.campo6
eso era lo que faltaba utilizar era una funcion MIN()/MAX() para obtener el resultado esperado, ya habia conseguido la solucion a la primera parte del problema.

La segunda parte es mas sencilla, eliminar los registros duplicados, esto lo consegui de la siguiente forma: creando una tabla temporal donde se registraran los datos unicos, luego se elimina los datos de la tabla principal y luego se vuelcan los datos de la tabla temporal a la principal como tabla temporal (en PostgreSQL):
CREATE TEMP TABLE temporal AS SELECT b.* FROM ( SELECT campo1, min(campo6) as campo6 FROM prueba2 GROUP BY campo1 ) a, prueba2 b WHERE a.campo1=b.campo1 AND a.campo6=b.campo6
o sencillamente asi: (de esta forma funciona en PostgreSQL y SQL Server)
SELECT b.* INTO temporal FROM ( SELECT campo1, min(campo6) as campo6 FROM prueba2 GROUP BY campo1 ) a, prueba2 b WHERE a.campo1=b.campo1 AND a.campo6=b.campo6;
luego eliminamos los datos (la funcion truncate tambien es soportada por sql server o almenos eso dicen los manuales)
TRUNCATE prueba2;
y por ultimo
INSERT INTO prueba2 SELECT * FROM temporal;
En postgreSQL creandose la tabla temporal no hubiera sido necesario eliminar la entidad, dado que esta desaparece cuando se termina la sesion, pero no siendo el caso para la sentencia INSERT [..] INTO [], entonces:
DROP TABLE temporal;
Bueno aunque lo anterior no es el santo grial, es un truco para salir de apuros o para alguien que no conociera una forma de hacerlo, espero que a alguien le sirva, almenos a mi me sirvio.

2 comentarios:

Anónimo dijo...

MIL TRILLONES DE GRACIAS NO TE IMAGINA EN CUANTAS PAGINA HE NAVEGADO PARA ENCONTRAR LAS RESPUESTA, CUAL QUIER COSA EN QUE TE PUEDA AYUDAR ESTE ES MI E-MAIL: dreams9j@hotmail.com

Marcel Rojas

Anónimo dijo...

Took me time to read the whole article, the article is great but the comments bring more brainstorm ideas, thanks.

- Johnson