Una crítica al lenguaje SQL (parte 2)

Definiciones
miércoles, 13 de marzo de 2013

En un artículo anterior comentaba que el lenguaje SQL era un excesivamente reiterativo, y ponía algunos ejemplos muy concretos:

  • Las mismas fórmulas y expresiones se repiten una y otra vez en las sentencias (en las columnas del SELECT, en los GROUP BY,…). Si la fórmula es compleja rápidamente se obtiene código difícil de mantener.
  • Los mismos JOINS se repiten en todas las sentencias. A pesar de que la relación entre las tablas es un conocimiento estático -propio del modelo de datos- debemos repetir las mismas relaciones PK-FK una y otra vez, una y otra vez, una y otra vez…
  • Operaciones entre filas. Utilizando SQL es muy sencillo hacer operaciones entre columnas (calcular el precio medio a partir del importe y las unidades, por ejemplo), pero es farragoso hacer operaciones entre filas (como comparar las ventas de este año respecto las del año anterior).

En aquella ocasión utilicé la estrella de FactResellerSales (de “Adventure Works DW”) para mostrar los ejemplos. Hoy, además, utilizaré las tablas de la estrella de “ventas de internet”:

Para el ejemplo de hoy, utilizaremos la estrella de FactIntenertSales

Operaciones entre distintas tablas de hecho

Una necesidad tan común como comparar las ventas respecto el presupuesto no es trivial resolverla con SQL. El lenguaje SQL es bueno agrupando, ordenando, y filtrando valores de un conjunto de datos (una tabla de hechos con sus tablas de dimensión, en concreto), pero si queremos comparar los valores 2 tablas de hecho, tendremos que hacer 2 consultas.

Por ejemplo, si queremos comparar o sumar las ventas de internet con las ventas en tiendas tendremos que escribir cosas así:

-- Ejemplo 5

WITH
isales1 AS (
  SELECT
    DimDate.CalendarYear AS CalendarYear,
    sum(isales.SalesAmount) AS ventasInternet
  FROM FactInternetSales isales
  INNER JOIN DimDate ON (isales.OrderDateKey=DimDate.DateKey)
  INNER JOIN DimCustomer ON (isales.CustomerKey=DimCustomer.CustomerKey)
  INNER JOIN DimGeography ON (DimCustomer.GeographyKey=DimGeography.GeographyKey)
  WHERE DimGeography.SpanishCountryRegionName='Francia'
  GROUP BY DimDate.CalendarYear
),
sales1 AS (
  SELECT
    DimDate.CalendarYear AS CalendarYear,
    sum(sales.SalesAmount) AS ventasReseller
  FROM FactResellerSales sales
  INNER JOIN DimReseller ON (sales.ResellerKey=DimReseller.ResellerKey)
  INNER JOIN DimGeography ON (DimReseller.GeographyKey=DimGeography.GeographyKey)
  INNER JOIN DimDate ON (sales.OrderDateKey=DimDate.DateKey)
  WHERE DimGeography.SpanishCountryRegionName='Francia'
  GROUP BY DimDate.CalendarYear
),
COMUN AS (
  SELECT DISTINCT CalendarYear FROM isales1
  UNION SELECT DISTINCT CalendarYear FROM sales1
)
SELECT
  COMUN.CalendarYear,
  isales1.ventasInternet AS ventasInternet,
  sales1.ventasReseller AS ventasReseller,
  coalesce(sales1.ventasReseller,0)+coalesce(isales1.ventasInternet,0) AS ventas
FROM COMUN
LEFT JOIN isales1 ON COMUN.CalendarYear=isales1.CalendarYear
LEFT JOIN sales1 ON COMUN.CalendarYear=sales1.CalendarYear

Por supuesto, en entornos Business Intelligence, estas necesidades no son excepcionales. Tanto los usuarios (desde sus herramientas de Query&Reporting) como los técnicos que crean y mantienen los procesos ETL de carga del Data Warehouse, hacen este tipo de consultas habitualmente (o no las saben hacer, y hacen cosas peores…).

La punta del iceberg

Tal vez las cosas que he comentado en este artículo te han parecido minucias o cosas inevitables. En mi opinión, son solo la punta del iceberg de un problema muy gordo.

He analizado solo las sentencias SELECT pero los mismos problemas, y de manera mucho más pronunciada, se repiten en las instrucciones de manipulación de la información (UPDATES, INSERTS y DELETES). Necesitaría un blog entero solo para hablar de ello… Piensa en el código necesario para hacer una carga de una dimensión SCD, por ejemplo…

En mi opinión, estas características del SQL son las que explican buena parte de los costes de desarrollo. Si el SQL fuera más imperativo, más legible, más fácil de escribir, el uso de herramientas ETL no estaría justificado, ya que los desarrollos serían mucho más fáciles.

Porque el SQL es como es, nacieron las herramientas de Query & Reporting, y nacieron posteriormente las herramientas ETL. Sin embargo, las herramientas ETL solo han cumplido parcialmente sus promesas. A pesar de su sofisticación, los procesos ETL siguen representando el 70% de los costes de implantación de un sistema Business Intelligence.

En otros ámbitos de la informática la evolución ha sido muy diferente. Los lenguajes han evolucionado muchísimo y han aparecido nuevos dialectos y sabores. Gracias a ello, un estudiante hoy en día puede hacer una aplicación en 2 tardes que hace 3 décadas hubiese necesitado un ejército de programadores de IBM… ¿Por qué la evolución del SQL ha sido tan limitada? No lo sé, pero sé que pagamos las consecuencias de ello…

Crono SQL

Como supongo que habrás notado, las sentencias SQL que aparecen en este artículo no las he escrito directamente. He utilizado el generador de consultas de Crono Analytics.

Si lees este blog, seguramente sepas que Crono Analytics es una solución de Business Intelligence fácil de usar. Al igual que todas las herramientas de BI, lo que hacemos es convertir las consultas de los usuarios al lenguaje SQL, pero nosotros tenemos un “truco”…. Internamente, Crono Analytics utiliza el lenguaje Crono SQL que resuelve las problemáticas que describo en este artículo. Hablaré de ello en la tercera parte de esta serie.

PD: Si te ha gustado este artículo, y no quieres perderte la finalización de esta serie, suscríbete al newsletter de Business Intelligence fácil.