Usando alias externo en una subconsulta

11
|    payments    |  | transactions |  | transaction_items |
|:--------------:|  |:------------:|  |:-----------------:|
|       id       |  |      id      |  |         id        |
|      date      |  |    number    |  |   transaction_id  |
|     amount     |  |     date     |  |    description    |
| transaction_id |  |      val     |  |       price       |
                                      |      discount     |
                                      |      quantity     |

Estoy tratando de mostrar una lista de pagos realizados en transacciones y mostrar el saldo actual después de cada pago. A continuación se muestra un ejemplo del resultado esperado.

| number | DATE(p.date) | total   | paid    | balance | 
| 1355   | 2016-10-31   | 899.00  | 450.00  | 449.00  | 
| 1355   | 2016-12-06   | 899.00  | 449.00  | 0.00    | 
| 1359   | 2016-09-28   | 4045.00 | 1515.00 | 2530    | 
| 1359   | 2016-10-24   | 4045.00 | 35.00   | 2495.00 | 
| 1361   | 2016-09-28   | 1548.00 | 1548.00 | 0.00    | 

y aquí está mi consulta hasta ahora, pero tengo un error en la cláusula where

select
    t.number,
    DATE(p.date),
    ti.total 'total',
    SUM(p.amount) 'paid',
    ti.total - paid.total 'balance'
from payments p
left join transactions t
on p.transaction_id = t.id
left join (
    select inner_ti.transaction_id, sum((inner_ti.price - inner_ti.discount) * inner_ti.quantity)  'total'
    from transaction_items inner_ti
    group by inner_ti.transaction_id
) ti on t.id = ti.transaction_id
left join (
    select inner_p.transaction_id, sum(inner_p.amount) 'total'
    from payments inner_p
    where inner_p.date <= p.date -- error unknown column p.date
    group by inner_p.transaction_id
) paid on t.id = paid.transaction_id
group by t.number, DATE(p.date), ti.total, paid.total
order by DATE(p.date) ASC

Tenga en cuenta que me estoy agrupando p.dateya que nuestra preocupación son los pagos totales realizados dentro del día.

¿Alguien puede aclararme por qué me sale ese error? ¿Y hay alguna solución para lograr el resultado esperado?

Jaime Sangcap
fuente

Respuestas:

10

Las dos selecciones anidadas en su consulta se denominan tablas derivadas . Una tabla derivada no debe correlacionarse con otros conjuntos de datos que participan en la consulta, por lo tanto, no se permiten referencias externas a ellos en la consulta anidada.

Una forma de resolver el problema es reescribir su consulta para mover la selección infractora al contexto donde se permite la correlación. En su caso, puede mover la subconsulta infractora a la cláusula SELECT:

select    t.number,
          DATE(p.date),
          ti.total 'total',
          SUM(p.amount) 'paid',
          ti.total - (select sum(inner_p.amount)
                      from     payments inner_p
                      where    inner_p.transaction_id = p.transaction_id
                      and      inner_p.date <= p.date
                     ) 'balance'
from      payments p
left join transactions t
on        p.transaction_id = t.id
left join (
          select   inner_ti.transaction_id, 
                   sum((inner_ti.price - inner_ti.discount) * inner_ti.quantity)  'total'
          from     transaction_items inner_ti
          group by inner_ti.transaction_id
          ) ti 
on        t.id = ti.transaction_id
group by  t.number, DATE(p.date), ti.total, 'balance'
order by  DATE(p.date) ASC;

rextester aquí


En aras de la exhaustividad, el estándar SQL en realidad tiene una sintaxis que permite la correlación para las tablas derivadas. Se llama unión lateral . Desde el punto de vista sintáctico, se ve casi exactamente como una unión normal, solo necesita agregar la LATERALpalabra clave después de JOIN:


left join lateral (
    select inner_p.transaction_id, sum(inner_p.amount) 'total'
    from payments inner_p
    where inner_p.date <= p.date -- this outer reference would be valid
    group by inner_p.transaction_id
) paid on t.id = paid.transaction_id

La palabra clave agregada marca la diferencia, ya que solo con esa palabra clave se permite que una consulta anidada haga referencia a otros conjuntos de datos en la misma cláusula FROM (a la izquierda de la palabra clave JOIN más reciente).

Las uniones laterales son compatibles actualmente con PostgreSQL y Oracle. SQL Server también admite un concepto similar con una sintaxis ligeramente diferente (y menos flexible). Como habrás adivinado, MySQL actualmente no admite nada por el estilo.

McNets
fuente
MariaDB admite funciones de ventana que pueden ser útiles para ejecutar problemas de totales como este: mariadb.com/kb/en/library/window-functions
ypercubeᵀᴹ
Mainstream MySQL tendrá la función de ventana en la versión 8: dev.mysql.com/doc/refman/8.0/en/window-functions.html Mi conjetura para cuándo es este año, probablemente en los primeros 6 meses (considerando que lo anterior dice: "Borrador de disponibilidad previa general: 12-01-2018").
ypercubeᵀᴹ
@ McNets y Andriy Lo tengo funcionando ahora usando tu respuesta. Lo explicaste bien y con algunas conclusiones (palabra clave lateral). ¡Gracias!
Jaime Sangcap
Me alegra poder ayudar.
McNets
@JaimeSangcap: feliz de ayudar, saludos.
Andriy M