Multiples counts en un solo query

Hace ya más de una año que empecé a trabajar en desarrollo web. Unos de los sitios que trabajé fue un sistema para enviar tarjetas postales y recuerdo el día en que me tocó ver como le hacía para hacer que un query que hacía varios counts sobre una misma tabla pasara de tardarse entre 3 minutos y 5 minutos a menos de un minuto (que es lo más que quería esperar mi jefe).

Era una tabla de regular tamaño, no con millones pero si con cientos de miles de tuplas, y recorrerla varias veces era un martirio, pues eso hacía que tardara tanto el query.

Probablemente hayan varias maneras de hacerlo, pero yo les presentaré 2 que fueron las me dieron resultado.

Solo antes de empezar, les comento que yo utilizo PostreSQL, la razón se las explicaré algún otro momento, asi que tomenlo muy en cuenta, si es que no encuentran las funciones que mencione en algunos de mis tips.

Vamos a imaginar que la tabla tiene la siguiente estructura y los siguientes datos (Adjunto los scripts):

id
color
forma
estado
marca
1rojocuadradoinactivonike
2azulredondodormidoadidas
3cafecuadradoactivomcdonalds
4negrotriangulardormidonike
5rojoredondoactivodell
6azulredondoactivoadidas
7blancocubicainactivonintendo
8moradotriangularactivomicrosoft
9verdecuadradodormidodell
10negrorectangularactivomac

Por ejemplo, si queremos contar cuantos hay en cada estado podemos ejecutar:

SELECT estado,COUNT(estado)
FROM multiplecount
GROUP BY estado

Lo cual nos da:

Estado
Count()
activo
5
dormido
3
inactivo
2

Aja!!, pero si queremos contar cuantos activos, cuantos dormidos, cuantos redondos y cuantos nike hay, allí ya se complica y usamos

  • El tip del CASE

SELECT
COUNT(CASE estado WHEN 'activo' THEN 1 ELSE null END) AS activos,
COUNT(CASE estado WHEN 'dormido' THEN 1 ELSE null END) AS dormidos,
COUNT(CASE forma WHEN 'redondo' THEN 1 ELSE null END) AS redondos,
COUNT(CASE marca WHEN 'nike' THEN 1 ELSE null END) AS nike
FROM multiplecount

Lo cual nos da:

activos
dormidos
redondos
nike
5
3
3
2

Este query hace uso de la premisa que la función COUNT() únicamente cuenta valores no nulos, por lo que cuando algo no cumple la condición le asignamos NULL y no lo cuenta, y si cumple la condición entonces le asignamos un valor, en este caso 1, para que lo cuente.

La otra forma de hacerlo es usando

  • El tip del NULLIF

SELECT
COUNT(NULLIF(estado='activo',false)) AS activos,
COUNT(NULLIF(estado='dormido',false)) AS dormidos,
COUNT(NULLIF(forma='redondo',false)) AS redondos,
COUNT(NULLIF(marca='nike',false)) AS nike
FROM multiplecount

Esto ya se complica un poco más, si es que no han usado la función NULLIF, o su equivalente en el dbms de su elección.

NULLIF devuelve null si los parametro son iguales, y devuelve el primer parametro si son distintos. Veamoslo despacio y con dibujitos, como lo diriamos comunmente:

Si hacemos NULLIF('el mundo es redondo',falso) nos devolvería el primer parametro: ‘el mundo es redondo’ ya que eso es verdadero y el segundo parametro es falso.
Si hacemos NULLIF('el mundo es cuadrado',falso) nos devolvería NULL, ya que ‘el mundo es cuadrado’ es falso y el segundo parametro es falso, por ser iguales retornaría NULL.

Lo mismo ocurre con NULLIF(estado='activo',false)) en las tuplas en que estado sea ‘activo’ el valor sera verdadero y como el segundo parametro es false entonces retorna true (que es el valor de evaluar esatdo=’activo’ para las tuplas que cumplan con esa condición). Y cómo true es un valor no nulo COUNT() lo tomaría en cuenta, en cambio en los valores en que estado<>’activo’ retornara false y como el segundo parametro es false, y esos valores son iguales, entonces retornara NULL, por los que count() no los tomará en cuenta.

Estas son las dos formas en que yo hago multiples counts en un mismo query sobre una tabla, prácticamente son la misma cosa, pero unos dicen papas y otros patatas, jeje.

Espero que les sirva, y que algún día los saque de apuros, jejeje.

Hasta la próxima.

Gracias a kementeus, pues el fue quien me enseño la forma usando el NULLIF.