Select Star SQL

Posible inocencia

Los opositores a la pena de muerte han argumentado que el riesgo de ejecutar por error a una persona inocente es un costo demasiado grande. En este capítulo intentamos aproximar cuántas personas inocentes pueden haber sido ejecutadas.

La principal salvedad es que una declaración de inocencia, incluso hecha en lecho de muerte, no constituye inocencia. Además, incluso si el recluso dice la verdad, hay muchas interpretaciones de inocencia: el recluso puede haber sido acusado de matar a dos personas pero ser inocente de matar a una sola; o puede haber matado al transeúnte pero no al agente. En Texas, el asesinato por sí solo no siempre conlleva la pena de muerte; debe tratarse de un delito capital, por lo que la situación puede ser compleja.

No obstante, sigue siendo inquietante que las declaraciones de inocencia persistan hasta el umbral de la ejecución cuando casi no queda nada por ganar. Nuestra tarea aquí es calcular con qué frecuencia ocurre esto calculando la proporción de declaraciones finales que contienen una reclamación de inocencia.


Funciones de agregación

Hay dos números que necesitamos para calcular la proporción:

  Numerador: Número de ejecuciones con reclamaciones de inocencia.

  Denominador: Número total de ejecuciones.

Hasta ahora, cada fila en la salida provenía de una sola fila de entrada. Sin embargo, aquí tanto el numerador como el denominador requieren información de múltiples filas. Esto nos dice que necesitamos usar una función de agregación. Agregar significa combinar múltiples elementos en un todo; de forma similar, las funciones de agregación toman varias filas y las combinan en un solo número.


La función COUNT

COUNT es probablemente la función de agregación más utilizada. Como su nombre indica, cuenta cosas. Por ejemplo, COUNT(<column>) devuelve el número de filas no nulas en la columna.

Como puedes ver, la función COUNT está ligada al concepto de NULL. Hagamos una pequeña digresión para aprender sobre NULLs.

Nulos

En SQL, NULL es el valor de una entrada vacía. Esto es diferente de la cadena vacía '' y del entero 0, ambos no considerados NULL. Para comprobar si una entrada es NULL, usa IS y IS NOT en lugar de = y !=.

Con esto, podemos encontrar el denominador de nuestra proporción:


Variaciones de COUNT

Hasta ahora todo bien. Pero ¿y si no sabemos qué columnas están libres de NULL? ¿O si ninguna lo está? ¿Cómo obtener la longitud de la tabla?

La solución es COUNT(*). Esto recuerda a SELECT * donde * representa todas las columnas. En la práctica COUNT(*) cuenta filas siempre que alguna de sus columnas no sea nula. Esto ayuda a obtener la longitud de la tabla porque no debería haber filas completamente nulas.

Otra variación común es contar un subconjunto de la tabla. Por ejemplo, contar ejecuciones del condado Harris: SELECT COUNT(*) FROM executions WHERE county='Harris'. Pero ¿qué si queremos simultáneamente contar las ejecuciones del condado Bexar?

La solución es usar CASE WHEN, que actúa como un gran if-else. Tiene el formato:

CASE
  WHEN <clause> THEN <result>
  WHEN <clause> THEN <result>
  ...
  ELSE <result>
END

Es una parte algo tosca de SQL; un error común es olvidar el END o la cláusula ELSE.


Práctica

Es útil pensar en cómo el motor ejecuta estas consultas: la versión con WHERE filtra primero y luego agrega, la versión COUNT+CASE WHEN recorre una vez, y la versión de doble COUNT lo recorre dos veces. Así que la primera suele ser la más eficiente.

Buscar documentación

Este libro no pretende ser una referencia exhaustiva de SQL. Para eso tendrás que mirar otros recursos en línea. Es una habilidad importante porque seguirás consultando documentación incluso después de familiarizarte con el lenguaje.

Con los modelos mentales que enseñamos aquí, las búsquedas deberían ser rápidas: normalmente solo comprobarás detalles como si la función se llama AVERAGE o AVG, en lugar de elegir un enfoque desde cero.

Para búsquedas suelo usar W3Schools, Stack Overflow y la documentación oficial de SQLite.

SELECT DISTINCT no es estrictamente una función de agregación porque no devuelve un único número y opera sobre la salida de la consulta. Sin embargo, lo incluimos porque comparte la característica de operar sobre múltiples filas.


Una consulta extraña

Antes de terminar, veamos esta consulta: SELECT first_name, COUNT(*) FROM executions.

¿No parece extraña? Si tienes un buen modelo mental de agregaciones, ¡debería! COUNT(*) intenta devolver un único número (la longitud de la tabla) mientras que first_name intenta devolver un valor por fila. ¿Debería la computadora devolver uno o muchos resultados? Si devuelve uno, ¿qué first_name elige? Si devuelve muchos, ¿debe replicar el COUNT(*) en cada fila? Las formas no coinciden.

En la práctica, las bases de datos tratan de devolver algo razonable. En nuestro caso, la base de datos elige el nombre correspondiente a la última fila de la tabla. Dado que la tabla está en orden cronológico inverso, la última fila es Charlie Brooks Jr. Las diferentes bases de datos pueden comportarse distinto, así que no conviene confiar en este comportamiento implícito.

Dialectos de SQL y bases de datos

Aunque llamamos a esto un libro sobre SQL, en realidad es un libro sobre SQLite. SQL es una idea general; en la práctica existen dialectos que implementan distintas partes del estándar.

El estándar no especifica todo: por ejemplo, no dicta si la función de longitud se llama LEN (SQL Server) o LENGTH (SQLite), ni cómo se deben citar identificadores. Por eso hay diferencias entre sistemas.

Hemos elegido SQLite porque es común y porque podemos ejecutar una base de datos en el cliente del navegador para esta página interactiva. Nos centramos en la funcionalidad central y en los modelos mentales, que son transferibles entre dialectos.


Conclusión y resumen

Usemos lo aprendido para completar nuestra tarea:

Este método es impreciso porque la inocencia puede expresarse de otras formas como “not guilty”. Aun así, probablemente subestima el número real y da una estimación aproximada. La pregunta que queda es si estamos dispuestos a aceptar la posibilidad de que hasta un 5% de las personas ejecutadas sean inocentes. (Paul Graham no está dispuesto.)

Para recapitular, hemos pasado de operaciones a nivel de fila a funciones de agregación sobre múltiples filas. Esto abre la posibilidad de estudiar comportamientos a nivel de sistema. En el siguiente capítulo aprenderemos a aplicar agregaciones por subgrupos usando GROUP BY.