Subido por Juan Luis Caballero

MySQL - PRIMEROS PASOS

Anuncio
Repaso Express
Consultas con MySQL
SQL
(Structured Query Language)
•
Es un lenguaje UNIVERSAL para “hablar” con bases de datos
relacionales.
•
Nos permite armar complejos reportes con los datos de las
tablas contenidas en la base.
•
Existen múltiples sistemas de gestión de bases de datos,
como MySQL, MS SQL Server, PostgreSQL, Oracle,
Snowflake, entre otros.
Contents
1
Estructura de un query
2
Window Functions
3
Subqueries, Common Table
Expressions (CTEs) & UNIONS
3
U2 – Tema 1
Estructura de un query
Estructura básica de un
query de SQL
Calculemos el número, monto y el ticket
promedio, de las transacciones PAGADAS
en el mes de Junio 2022, agrupado por
sexo y tipo de cuenta.
Mostrar sólo los registros que tengan más
de 20k transacciones y ordenar en forma
descendente por monto de TPV.
SELECT
cl.sexo,
cu.tipo,
COUNT(transaccion_id) AS txn,
SUM(t.monto)
AS tpv,
SUM(t.monto) / COUNT(transaccion_id)
AS tkt_promedio
FROM
transacciones
t
INNER JOIN cuentas
cu
ON cu.cuenta_id = t.cuenta_id
INNER JOIN clientes
cl
ON cl.cliente_id = cu.cliente_id
WHERE t.estatus = 'PAGADA'
AND t.fecha_transaccion_utc >= '2022-06-01'
AND t.fecha_transaccion_utc < '2022-07-01'
GROUP BY cl.sexo , cu.tipo
HAVING COUNT(transaccion_id) > 20000
ORDER BY tpv DESC;
5
SELECT
•
Sentencia inicial de todo query en SQL.
•
Se usa para SELECCIONAR las
columnas de la “tabla fuente” que
serán desplegadas en el output del
query.
•
Para crear columnas, podemos:
•
realizar operaciones entre
columnas.
usar funciones con sus valores.
•
Referencias sobre funciones
•
SELECT
cl.sexo,
cu.tipo,
COUNT(transaccion_id) AS txn,
SUM(t.monto)
AS tpv,
SUM(t.monto) / COUNT(transaccion_id)
AS tkt_promedio
FROM
transacciones
t
INNER JOIN cuentas
cu
ON cu.cuenta_id = t.cuenta_id
INNER JOIN clientes
cl
ON cl.cliente_id = cu.cliente_id
WHERE t.estatus = 'PAGADA'
AND t.fecha_transaccion_utc >= '2022-06-01'
AND t.fecha_transaccion_utc < '2022-07-01'
GROUP BY cl.sexo , cu.tipo
HAVING COUNT(transaccion_id) > 20000
ORDER BY tpv DESC;
6
FROM
•
Se usa para especificar la tabla en
donde se almacenan los datos de las
columnas que serán desplegadas en el
SELECT.
•
Es buena práctica identificar con un
ALIAS a las tablas del FROM.
•
A la tabla generada en el join le
diremos “tabla fuente”.
SELECT
cl.sexo,
cu.tipo,
COUNT(transaccion_id) AS txn,
SUM(t.monto)
AS tpv,
SUM(t.monto) / COUNT(transaccion_id)
AS tkt_promedio
FROM
transacciones
t
INNER JOIN cuentas
cu
ON cu.cuenta_id = t.cuenta_id
INNER JOIN clientes
cl
ON cl.cliente_id = cu.cliente_id
WHERE t.estatus = 'PAGADA'
AND t.fecha_transaccion_utc >= '2022-06-01'
AND t.fecha_transaccion_utc < '2022-07-01'
GROUP BY cl.sexo , cu.tipo
HAVING COUNT(transaccion_id) > 20000
ORDER BY tpv DESC;
7
JOIN
•
Parte opcional del FROM.
•
Nos permite unir dos o más tablas para
acceder a sus datos desde un solo
query. La “tabla fuente” se crea con
TODOS los registros que satisfagan la
condición del ON.
•
Es decir, con el join se “crea”
internamente una nueva tabla.
SELECT
cl.sexo,
cu.tipo,
COUNT(transaccion_id) AS txn,
SUM(t.monto)
AS tpv,
SUM(t.monto) / COUNT(transaccion_id)
AS tkt_promedio
FROM
transacciones
t
INNER JOIN cuentas
cu
ON cu.cuenta_id = t.cuenta_id
INNER JOIN clientes
cl
ON cl.cliente_id = cu.cliente_id
WHERE t.estatus = 'PAGADA'
AND t.fecha_transaccion_utc >= '2022-06-01'
AND t.fecha_transaccion_utc < '2022-07-01'
GROUP BY cl.sexo , cu.tipo
HAVING COUNT(transaccion_id) > 20000
ORDER BY tpv DESC;
8
Tipos de Joins en MySQL
Los JOINS se clasifican de acuerdo a los registros que se mantienen en la “tabla fuente”
dependiendo de si se cumple la condición en el ON.
INNER
RIGHT / LEFT
CROSS
Mantiene los registros
que satisfacen la
condición.
Toma una tabla como
pivote, y agrega los
registros de la otra que
satisfagan la condición.
Inserta NULLs cuando
no hay coincidencias.
Realiza TODAS las
combinaciones posibles
entre los registros de
ambas tablas.
No es necesario el ON.
Link para más detalle sobre los JOINs.
9
WHERE
•
Con esta sentencia filtramos a la
“tabla fuente”.
•
Es decir, en el WHERE establecemos las
condiciones para mantener o no un
registro en nuestra “tabla fuente”.
SELECT
cl.sexo,
cu.tipo,
COUNT(transaccion_id) AS txn,
SUM(t.monto)
AS tpv,
SUM(t.monto) / COUNT(transaccion_id)
AS tkt_promedio
FROM
transacciones
t
INNER JOIN cuentas
cu
ON cu.cuenta_id = t.cuenta_id
INNER JOIN clientes
cl
ON cl.cliente_id = cu.cliente_id
WHERE t.estatus = 'PAGADA'
AND t.fecha_transaccion_utc >= '2022-06-01'
AND t.fecha_transaccion_utc < '2022-07-01'
GROUP BY cl.sexo , cu.tipo
HAVING COUNT(transaccion_id) > 20000
ORDER BY tpv DESC;
10
GROUP BY
•
La sentencia GROUP BY indica los
grupos sobre los que se realizarán
las operaciones del SELECT.
•
Cada grupo está formado por las
combinaciones de valores de las
columnas indicadas en el GROUP BY.
•
El output final del query tendrá a lo
más un registro por cada combinación
de valores.
SELECT
cl.sexo,
cu.tipo,
COUNT(transaccion_id) AS txn,
SUM(t.monto)
AS tpv,
SUM(t.monto) / COUNT(transaccion_id)
AS tkt_promedio
FROM
transacciones
t
INNER JOIN cuentas
cu
ON cu.cuenta_id = t.cuenta_id
INNER JOIN clientes
cl
ON cl.cliente_id = cu.cliente_id
WHERE t.estatus = 'PAGADA'
AND t.fecha_transaccion_utc >= '2022-06-01'
AND t.fecha_transaccion_utc < '2022-07-01'
GROUP BY cl.sexo , cu.tipo
HAVING COUNT(transaccion_id) > 20000
ORDER BY tpv DESC;
11
HAVING
•
Es un segundo filtro aplicado sobre el
resultado tras aplicar el GROUP BY y el
SELECT.
•
A diferencia del WHERE, el filtro se
puede aplicar sobre el resultado de
una función de agregación, por
ejemplo de un SUM o un COUNT..
SELECT
cl.sexo,
cu.tipo,
COUNT(transaccion_id) AS txn,
SUM(t.monto)
AS tpv,
SUM(t.monto) / COUNT(transaccion_id)
AS tkt_promedio
FROM
transacciones
t
INNER JOIN cuentas
cu
ON cu.cuenta_id = t.cuenta_id
INNER JOIN clientes
cl
ON cl.cliente_id = cu.cliente_id
WHERE t.estatus = 'PAGADA'
AND t.fecha_transaccion_utc >= '2022-06-01'
AND t.fecha_transaccion_utc < '2022-07-01'
GROUP BY cl.sexo , cu.tipo
HAVING COUNT(transaccion_id) > 20000
ORDER BY tpv DESC;
12
ORDER BY
•
El ORDER BY se usa para ordenar el
output final del query.
•
Para el ordenamiento se puede usar
cualquiera de las columnas generadas
con el SELECT.
•
Cuando se tienen múltiples columnas
en el ORDER BY, el ordenamiento se
aplica de izquierda a derecha.
SELECT
cl.sexo,
cu.tipo,
COUNT(transaccion_id) AS txn,
SUM(t.monto)
AS tpv,
SUM(t.monto) / COUNT(transaccion_id)
AS tkt_promedio
FROM
transacciones
t
INNER JOIN cuentas
cu
ON cu.cuenta_id = t.cuenta_id
INNER JOIN clientes
cl
ON cl.cliente_id = cu.cliente_id
WHERE t.estatus = 'PAGADA'
AND t.fecha_transaccion_utc >= '2022-06-01'
AND t.fecha_transaccion_utc < '2022-07-01'
GROUP BY cl.sexo , cu.tipo
HAVING COUNT(transaccion_id) > 20000
ORDER BY tpv DESC;
13
En resumen…
¿Qué columnas necesito?
¿De dónde tomaré la info?
¿Qué filtros aplicaré a la info?
¿Cómo agruparé la info?
¿Con qué registros me quiero quedar?
¿En qué orden veré mi tabla?
SELECT
cl.sexo,
cu.tipo,
COUNT(transaccion_id) AS txn,
SUM(t.monto)
AS tpv,
SUM(t.monto) / COUNT(transaccion_id)
AS tkt_promedio
FROM
transacciones
t
INNER JOIN cuentas
cu
ON cu.cuenta_id = t.cuenta_id
INNER JOIN clientes
cl
ON cl.cliente_id = cu.cliente_id
WHERE t.estatus = 'PAGADA'
AND t.fecha_transaccion_utc >= '2022-06-01'
AND t.fecha_transaccion_utc < '2022-07-01’
GROUP BY cl.sexo , cu.tipo
HAVING COUNT(transaccion_id) > 20000
ORDER BY tpv DESC;
¿Sabías que el orden en que se ejecuta el query no es el
mismo con el que se escribe?
Orden en que se escribe
1.
2.
3.
4.
5.
6.
SELECT, AGGREGATE FUNCTIONS
FROM, JOIN
WHERE
GROUP BY
HAVING
ORDER BY
Orden en que se ejecuta
1.
2.
3.
4.
5.
6.
7.
FROM, JOIN
WHERE
GROUP BY
AGGREGATE FUNCTIONS
HAVING
SELECT
ORDER BY
15
Y listo…ahora ya sabes lo
básico de SQL!!!
Referencias
1.
FUNCIONES Y OPERADORES
I.
Operadores
II. Funciones para controlar el flujo.
III. Funciones matemáticas y aritméticas.
IV. Funciones de fechas y Horas
V. Funciones para cadenas de texto
VI. Conversión de tipos de datos
VII. Funciones agregadoras (sólo las básicas)
VIII. Window Functions
**MUY IMPORTANTES (Y ÚTILES)
2.
Cheat Sheet de funciones de MySQL (ALTAMENTE
RECOMENDABLE).
3.
Ejercicios de Práctica ONLINE (ALTAMENTE
RECOMENDABLE).
4.
LIBRO SQL Practice Problems (ALTAMENTE
RECOMENDABLE).
U2 – Tema 2
Window Functions
Introducción
Para entender a las Window Functions, comencemos analizando a las Aggregate Functions:
Dada la siguiente tabla:
select cliente_id
, sexo
from banmaya.clientes
where cliente_id <= 100004;
¿Puedes construir un query que calcule esta
otra?
select sexo
, count(cliente_id) as num_clientes
from banmaya.clientes
where cliente_id <= 100004
group by 1;
Introducción
Para entender a las Window Functions, comencemos analizando a las Aggregate Functions:
Aggregate Functions
COUNT, SUM + GROUP BY
<
Hacen cálculos por grupos y
reducen registros
¿Cómo puedo agregar los porcentajes de hombres y mujeres como una nueva
columna?
select sexo
, count(cliente_id) as num_clientes
, count(cliente_id) / sum(count(cliente_id)) over () Window Functions
as num_clientes
from banmaya.clientes
COUNT, SUM + Window frame
where cliente_id <= 100004
group by 1;
Hacen cálculos por grupos y
NO reducen registros
Aggregate vs Window Functions
•
“Agregan” o reducen registros haciendo
un cálculo por grupos.
•
Se retornan los valores únicos de cada
grupo y el cálculo agregado.
•
Las usamos cuando se necesita mostrar la
información agrupada.
•
Asigna un valor que es calculado con base
a registros que guardan una relación. No
reducen registros.
•
No modifican la estructura original de la
tabla, y sólo agrega la columna con los
cálculos.
•
Las usamos para hacer operaciones entre
valores de distintas filas sin modificar la
tabla original.
Estructura
Query para construir la tabla
select transaccion_id
, estatus
, date_format(fecha_transaccion_utc, '%Y-%m-%d') fecha
, count(transaccion_id)
over (
partition by estatus
order by day(fecha_transaccion_utc)
rows between 2 preceding and 1 following
) as with_rows
from banmaya.transacciones
where cuenta_id = 200003
and fecha_transaccion_utc < '2021-05-10';
Estructura
window_function()
over (
partition by <…>
order by <…>
<window frame>
) as alias_columnas
Estructura
Window Function
Query para construir la tabla
select transaccion_id
, estatus
, date_format(fecha_transaccion_utc, '%Y-%m-%d') fecha
, count(transaccion_id)
over (
partition by estatus
order by day(fecha_transaccion_utc)
rows between 2 preceding and 1 following
) as with_rows
from banmaya.transacciones
where cuenta_id = 200003
and fecha_transaccion_utc < '2021-05-10';
•
•
•
•
•
•
•
Window Function
• Función que se aplicará sobre
el Window Frame.
• Se pueden usar aggregate
functions
convertidas
a
window
functions
o
funciones
que
son
exclusivamente
window
functions.
Aggregate
Functions
AVG()
COUNT()
MAX()
MIN()
STD()
VARIANCE()
SUM()
Window Functions
•
•
•
•
•
•
•
•
•
•
•
CUME_DIST()
DENSE_RANK()
FIRST_VALUE()
LAG()
LAST_VALUE()
LEAD()
NTH_VALUE()
NTILE()
PERCENT_RANK()
RANK()
ROW_NUMBER()
Estructura
Partition by
Query para construir la tabla
select transaccion_id
, estatus
, date_format(fecha_transaccion_utc, '%Y-%m-%d') fecha
, count(transaccion_id)
over (
partition by estatus
order by day(fecha_transaccion_utc)
rows between 2 preceding and 1 following
) as with_rows
from banmaya.transacciones
where cuenta_id = 200003
and fecha_transaccion_utc < '2021-05-10';
Partition by
• Divide la tabla en grupos o
particiones sobre los cuales
se aplicará la función.
• Puede construirse con una o
más columnas.
• Es opcional dentro de la
cláusula OVER. Cuando se
omite se toma a todo el
conjunto de datos como una
sola partición.
Estructura
Order by
Query para construir la tabla
select transaccion_id
, estatus
, date_format(fecha_transaccion_utc, '%Y-%m-%d') fecha
, count(transaccion_id)
over (
partition by estatus
order by day(fecha_transaccion_utc)
rows between 2 preceding and 1 following
) as with_rows
from banmaya.transacciones
where cuenta_id = 200003
and fecha_transaccion_utc < '2021-05-10';
Order by
• Ordena los registros
dentro de cada partición.
• Puede construirse con una o
más columnas.
• Para algunas funciones es
obligatorio incluirla.
Estructura
Window Frame
Window Frame
• Un Window Frame es un conjunto de filas relacionadas
de alguna forma con cada registro. Son calculadas por
separado dentro de cada partición.
Query para construir la tabla
select transaccion_id
, estatus
, date_format(fecha_transaccion_utc, '%Y-%m-%d') fecha
, count(transaccion_id)
over (
partition by estatus
order by day(fecha_transaccion_utc)
rows between 2 preceding and 1 following
) as with_rows
from banmaya.transacciones
where cuenta_id = 200003
and fecha_transaccion_utc < '2021-05-10';
Current row
Current row
• Es opcional dentro del OVER. Los valores por default
dependen de si se incluye o no el ORDER BY.
• No todas la funciones los admiten.
Un window frame tiene la siguiente estructura:
ROWS | RANGE BETWEEN límite_inferior AND límite_superior
 ROWS se usa para acotar por el número de filas.
 RANGE se usa para acotar por rangos de valores.
 Los límites se construyen con las siguientes palabras clave
(subrayadas en rojo):
¿Sabías que el orden en que se ejecuta el query no es el
mismo con el que se escribe?
Orden en que se escribe
1.
2.
3.
4.
5.
6.
SELECT, AGGREGATE FUNCTIONS,
WINDOW FUNCTIONS
FROM, JOIN
WHERE
GROUP BY
HAVING
ORDER BY
Orden en que se ejecuta
1.
2.
3.
4.
5.
6.
7.
8.
FROM, JOIN
WHERE
GROUP BY
AGGREGATE FUNCTIONS
HAVING
WINDOW FUNCTIONS
SELECT
ORDER BY
27
Aplicaciones más comunes
•
Se deja como ejercicio construir el query para replicar esta tabla. Usar el filtro cliente_id < 100010.
28
Apéndices
Window Functions
Window Frame con ROWS vs RANGE
Usamos RANGE cuando queremos definir ventanas de tamaño
variable que dependan de los valores de la columna de ordenación.
Usamos ROWS cuando queremos definir ventanas de tamaño
fijo que no dependan de los valores de la columna de
ordenación.
count(transaccion_id)
count(transaccion_id)
over (
partition by estatus
order by day(fecha_transaccion_utc)
range between 2 preceding and 1 following
) as with_range
over (
partition by estatus
order by day(fecha_transaccion_utc)
rows between 2 preceding and 1 following
) as with_rows
Día 7
Current row
Cuenta entre 2
filas previas
hasta la
siguiente
Current row
•
•
Link para más información.
Para replicar las tablas, tomar la estructura del query en esta diapositiva y cambiar las columnas correspondientes.
Cuenta donde
hayan días
entre 5 y 8
Día 8
Cuenta donde
hayan días
entre 6 y 9
Window Functions
Window Frame con RANGE y time frames
Anteriormente vimos que la cláusula RANGE dentro de un Window Frame se usa para hacer cálculos que dependan de los
valores en la columna de ordenación. Cuando la columna de ordenación es de tipo DATETIME, DATE o TIME podemos definir las
ventas por INTERVALOR DE TIEMPO.
count(transaccion_id)
over (
partition by estatus
order by day(fecha_transaccion_utc)
RANGE between INTERVAL 2 HOUR preceding
and INTERVAL 1 HOUR following
) as with_range_by_hour
, count(transaccion_id)
over (
partition by estatus
order by day(fecha_transaccion_utc)
RANGE between INTERVAL 2 DAY preceding
and INTERVAL 1 DAY following
) as with_range_by_day
•
•
Se puede usar cualquier intervalo de tiempo enlistado aquí.
Para replicar la tabla, tomar la estructura del query en esta diapositiva y agregar las columnas correspondientes.
Referencias
1.
Documentación Window Functions
2.
Cheat Sheet de window functions (ALTAMENTE
RECOMENDABLE). Ignorar la cláusula GROUP en
el Window Frame. No está disponible en MySQL.
U2 – Tema 3
Subqueries, Common Table
Expressions (CTEs) & UNIONS
Introducción
Se nos solicita construir un reporte con todas las transacciones mensuales de los clientes con ID < 100005 en TODO el año
2020.
Lo primero que se nos debe ocurrir es hacer lo siguiente:
select
date_format(t.fecha_transaccion_utc, "%y/%m") MES
, count(t.transaccion_id)
TXN
from clientes
cl
inner join cuentas
cu using(cliente_id)
inner join transacciones t using(cuenta_id)
where cliente_id < 100005 and year(fecha_transaccion_utc) =
group by 1
order by 1;
2020
¿Cómo puedo agregar los
meses de 2020 donde NO
hubieron transacciones?
Introducción
Solución:
1.
Repasemos de forma general el problema:
Queremos construir lo siguiente:
Pero sólo tenemos esto:
Identifica todos los
elementos/pasos necesarios
para construir la tabla final.
(Usualmente esto es lo más complicado)
2.
“Generar” la información
faltante: Crear las tablas (queries)
con las que se pueda construir la
tabla final.
3.
“Unir” todas las tablas
creadas con la información
existente.
•
•
•
4.
Subqueries
Common Table Expressions
(CTEs)
Set Operations (UNION,
INTERSECT, etc)
Obtener la tabla final
1. Identifica todos los elementos/pasos necesarios para construir la tabla final
En este punto debes pensar en todo lo que necesitas para generar la tabla final. Debes poder “imaginar” paso a paso la solución.
Paso
1
Tabla
MESES
Tabla
TXNS
Paso
2
Unimos las tablas
select *
from MESES
m
left join TXNS t
using(MES);
Paso
3
Construimos la tabla final
select m.MES
, ifnull(t.TXN, 0) TXN
from MESES
m
left join TXNS t
using(MES);
NOTA: Esta sólo es una solución de muestra, no es la final.
2. Generar la información faltante
Construye un query para generar cada tabla del Paso 1 por separado.
Paso
1
Tabla
MESES
Tabla
TXNS
Tabla TXNS
select
date_format(t.fecha_transaccion_utc, "%y/%m") MES
, count(t.transaccion_id)
TXN
from clientes
cl
inner join cuentas
cu using(cliente_id)
inner join transacciones t using(cuenta_id)
where cliente_id < 100005
and year(fecha_transaccion_utc) = 2020
group by 1
order by 1;
Tabla MESES
select distinct
date_format(t.fecha_transaccion_utc, "%y/%m") MES
from transacciones t
where year(fecha_transaccion_utc) = 2020
order by 1;
3. “Unir” todas las tablas creadas con la información existente.
En este paso podemos hacer uso de cualquiera de las siguientes técnicas:
Subquery: Consiste en anidar uno o más queries dentro de otro.
select *
from (
select distinct
date_format(t.fecha_transaccion_utc, "%y/%m") MES
from transacciones t
where year(fecha_transaccion_utc) = 2020
order by 1
)
MESES
left join (
select
date_format(t.fecha_transaccion_utc, "%y/%m") MES
, count(t.transaccion_id)
TXN
from clientes
cl
inner join cuentas
cu using(cliente_id)
inner join transacciones
t using(cuenta_id)
where cliente_id < 100005
and year(fecha_transaccion_utc) = 2020
group by 1
)
TXNS using(MES);
Resultado
Tabla MESES
Tabla TXNS
NOTA: NO SE RECOMIENDA ANIDAR QUERIES DENTRO DE OTROS QUE YA ESTÉN ANIDADOS.
3. “Unir” todas las tablas creadas con la información existente.
En este paso podemos hacer uso de cualquiera de las siguientes técnicas:
Common Table Expressions (CTEs): Consiste en generar secuencialmente una o más tablas
temporales, que pueden ser usadas repetidas veces dentro de los queries siguientes.
with MESES as (
select distinct
date_format(t.fecha_transaccion_utc, "%y/%m") MES
from transacciones t
where year(fecha_transaccion_utc) = 2020
order by 1
)
, TXNS as (
select
date_format(t.fecha_transaccion_utc, "%y/%m") MES
, count(t.transaccion_id)
TXN
from clientes
cl
inner join cuentas
cu using(cliente_id)
inner join transacciones
t using(cuenta_id)
where cliente_id < 100005
and year(fecha_transaccion_utc) = 2020
group by 1
)
select *
from MESES
m
left join TXNS t using(MES);
Resultado
Tabla MESES
Tabla TXNS
3. “Unir” todas las tablas creadas con la información existente.
En este paso podemos hacer uso de cualquiera de las siguientes técnicas:
UNION: “Une” los registros de dos tablas. Se pueden concatenar para unir
varias tablas.
select * from (
select distinct
date_format(t.fecha_transaccion_utc, "%y/%m") MES
, 0
TXN -- Columna nueva
from transacciones t
Tabla MESES
where year(fecha_transaccion_utc) = 2020
order by 1
)
MESES
UNION ALL
select * from (
select
date_format(t.fecha_transaccion_utc, "%y/%m") MES
, count(t.transaccion_id)
TXN
from clientes
cl
Tabla TXNS
inner join cuentas
cu using(cliente_id)
inner join transacciones
t using(cuenta_id)
where cliente_id < 100005
and year(fecha_transaccion_utc) = 2020
group by 1
)
TXNS;
Resultado
4. Obtener la tabla final.
Tras haber seleccionado la técnica que mejor se acomode al problema, sólo falta modificar el query para obtener el resultado
esperado. En este caso seleccionamos a las CTE:
with MESES as (
select distinct
date_format(t.fecha_transaccion_utc, "%y/%m") MES
from transacciones t
where year(fecha_transaccion_utc) = 2020
order by 1
)
, TXNS as (
select
date_format(t.fecha_transaccion_utc, "%y/%m") MES
, count(t.transaccion_id)
TXN
from clientes
cl
inner join cuentas
cu using(cliente_id)
inner join transacciones
t using(cuenta_id)
where cliente_id < 100005
and year(fecha_transaccion_utc) = 2020
group by 1
)
select m.MES, ifnull(t.txn, 0) TXN
-- Modificación
from MESES
m
left join TXNS t using(MES);
Resultado
NOTA: Se deja al interesado modificar las tablas con los Subqueries y UNION para obtener el mismo resultado.
Conclusiones y recomendaciones
Usa cualquiera de las técnicas anteriores cuando necesites construir tablas a partir de los resultados de uno o más queries,
siguiendo las siguientes recomendaciones:
Técnica
Subqueries
CTEs*
Set operations:
UNION
Se usa para:
•
•
Anidar dos o más queries SIMPLES.
Los queries anidados sólo se usan UNA
vez.
•
•
•
Queries más complejos.
Reutilizar los resultados de un mismo
query múltiples veces.
Obtener queries más fáciles de leer.
•
Unir los registros de dos o más queries.
Evitar:
•
•
Anidar subqueries dentro de
subqueries.
Usar queries muy complejos como
subqueries.
•
Crear demasiadas CTEs, ya que
complejizan la lectura.
•
Evitar esta técnica salvo para
construcciones muy específicas.
Conclusiones y recomendaciones
En general:
• Las CTEs es la técnica que más recomiendo en la práctica.
o Generan queries más fáciles de leer y en ocasiones más rápidos.
o Cuando se tienen grandes cantidades de datos, funcionan mejor que las Window Functions (AVANZADO).
• Puedes combinar cualquiera de estas técnicas, según lo necesites. Es decir, Subqueries dentro de CTEs, CTEs que
almacenen Subqueries, UNION de Subqueries con CTEs y todas las combinaciones que puedas imaginar.
• NO ABUSAR DE NINGUNA DE LAS TÉCNICAS. Recuerda que cada subquery o CTE se ejecuta
por separado, lo cual puede ser muy costoso computacionalmente. Usa sólo lo necesario.
Apéndices
CTEs Recursivos
Esta técnica nos permite generar secuencias de números, letras, fechas, etc, desde una CTE. Es útil cuando dichas
secuencias no se pueden generar con queries de la base de datos, o cuando es muy complicado/tardado obtenerlas.
Aquí tenemos dos ejemplos:
with RECURSIVE cte(n) as
(
select 1
union all
select n + 1 from cte where n < 5
)
select * from cte;
with RECURSIVE MESES(n, MES) as
(
select 1, '20/01’
union all
select n + 1
, date_format(date_add(date('2020-01-01'), INTERVAL n MONTH)
, "%y/%m")
from MESES where n < 12
)
select MES from MESES;
Tabla MESES
del ejemplo inicial.
•
Link para más información.
Referencias
1.
Subqueries (útil para aprender otras formas de
usar los subqueries).
2.
Documentación Common Table Expressions
3.
Set Operations con SQL (lectura ligera y
recomendable)
Descargar