Diferencia entre GROUP BY y DISTINCT, ¿Cuál es más rápido?

Muchas veces nos topamos con la tarea de saber cuales valores de cierta columna(s) son distintos de un conjunto de registros.

Normalmente lo hacemos con la clausula distinct, pues nos ahorra un par de palabras a la hora de escribir el query. Algo como:

select distinct(firstname) from test

Pero el mismo resultado obtenemos si lo hacemos de la siguiente forma:

select firstname from test group by firstname

Entonces, ¿cuál es la diferencia? La diferencia es que con una cantidad considerable de registros, podríamos reducir hasta 1 segundo en el query. Veámoslo con un ejemplo:

Tenemos la tabla test con los siguientes datos (Mostraré solo una parte de la tabla, para ver todos los datos vean el script):

Nombre
codigo1
codigo2
codigo3
Juan256536830835763
Juan87728636342562659891256518827635801
Mohamad25653346402565334648256533464735801
Joel2059330320205933640035205
William2058700123205870022735209
William2059789339205870022735594
William2058700123205870022735202
Elia2516079797205870022736608
Elia2516079797251639094036532
Elia2516079797251639094036608
Elia2516079797251639094036545

Esta tabla tiene 100 registros y cuando hacemos un DISTINCT sobre la columna firstname el query tarda 110ms en promedio
mientras si lo hacemos con GROUP BY 93 ms. ¿17 ms se preguntarán? Pero si eso sucede con 100 registros y sin nadie más usando la BD, imagínense lo significativo que es el cambio con 2 millones de registros y varias personas haciendo el mismo query al mismo tiempo.

¿A que se debe la diferencia?

Si vemos el plan de ejecución podríamos observar lo siguientes:


-- query con DISTINCT
'Unique (cost=37.41..39.91 rows=200 width=32) (actual time=0.380..0.462 rows=49 loops=1)'
' -> Sort (cost=37.41..38.66 rows=500 width=32) (actual time=0.379..0.409 rows=100 loops=1)'
' Sort Key: firstname'
' -> Seq Scan on test (cost=0.00..15.00 rows=500 width=32) (actual time=0.015..0.093 rows=100 loops=1)'
'Total runtime: 0.495 ms'

Aquí el costo de hacer un recorrido secuencial y luego sort, unique es el que aumenta en total el tiempo de ejecución, mientras


-- Haciendo un query con GROUP BY
'HashAggregate (cost=16.25..18.25 rows=200 width=32) (actual time=0.145..0.165 rows=49 loops=1)'
' -> Seq Scan on test (cost=0.00..15.00 rows=500 width=32) (actual time=0.013..0.052 rows=100 loops=1)'
'Total runtime: 0.201 ms'

Se realiza un recorrido secuencial pero se utiliza una función Hash que es más rápida que el sort y el unique.

Así que aunque nos lleve un poco más de tiempo escribir un query con GROUP BY, vamos a recuperarlo cada vez que se ejecute dicho query.

Bueno, pues, una vez más, espero que les sirva y hasta la próxima.

Por cierto, esperen una oleada de artículos entre hoy y mañana pues vamos tratar de subir el nivel del Blog.