0

como deberia construir la siguiente consulta? lo que necesito es un select donde me muestre el nombre del cliente y el total de la suma de las deudas que tiene, en caso de no tener deuda igual debe devolverme el nombre del cliente con deuda 0(cero),el nombre lo tomo de la tabla cliente, la tabla deuda me registra el id de la deuda, la fecha de la deuda y el cliente a quien corresponde la deuda, la tabla deuda producto me registra los productos relacionados a cada deuda y el precio de cada producto.

intente algo asi pero no me devuelve nada la consulta:

Select c.nombre, (select sum(precio) from DeudaxProducto) 
from Clientes c inner join Deudas d on c.id=d.cliente 
inner join DeudaxProducto dp on d.id=dp.iddeuda 
group by c.nombre

introducir la descripción de la imagen aquí

Agustin Coronel
  • 335
  • 3
  • 21
  • 1
    No entiendo el sentido de la tabla deudas... – gbianchi Feb 23 '19 at 04:52
  • la tabla deudas registra las deudas, es decir cada id deuda es unico, la fecha es la hora o momento en que se registro la deuda, el cliente es la persona a quien pertenece dicha deuda, en otras palabras, un cliente puede tener varias deudas, la tabla deudaxproducto lo que hace es decirme que productos pertenercen a una determinada deuda cuantos se registraron y a que precio – Agustin Coronel Feb 23 '19 at 04:56
  • una de esas dos tablas esta mal.. esto es lo que se llama cabera-productos. Tu tabla deuda, tiene un id de cabecera, un id de cliente, una fecha, mas datos de la factura... y despues la tabla de productos (o detalle) tiene un id propio de la tabla + id cabecera + id productos + precio... En general la tabla factura tiene el total, y los descuentos y esas cosas... – gbianchi Feb 23 '19 at 05:03
  • Y como recomendacion, te diria que vuelvas a hacer el [tour]. Tenes muchas preguntas sin votos positivos y sobre todo sin respuestas aceptadas. El sitio puede banearte automaticamente y prohibirte hacer preguntas si no arreglas ese problema. – gbianchi Feb 23 '19 at 05:34

3 Answers3

1

Lo que intentaste hacer vale, pero con unos cambios minimos:

  • Primero necesitas un left join de la tabla clientes, asi trae todos si o si.
  • Despues el select sum parece una buena idea, pero no lo necesitas. Podrias usarlo, pero de una forma que haria mucho mas complejo el query.

Entonces, deberias hacer algo asi:

Select c.nombre, sum(precio)
from Clientes c left join Deudas d on c.id=d.cliente 
inner join DeudaxProducto dp on d.id=dp.iddeuda 
group by c.nombre

Tene en cuenta lo que te remarque en los comentarios. La tabla deuda producto, si tiene una clave compuesta, hara mas dificil corregir errores en las facturas. Y la tabla deudas, deberia tener el total de la deuda.

gbianchi
  • 19,675
  • 11
  • 38
  • 58
  • gracias por la ayuda pero para dejar de romperme la cabeza con una consulta tan compleja, mejor agregue un campo mas a la tabla deuda donde se guarda el total de cada deuda – Agustin Coronel Feb 23 '19 at 05:27
  • 1
    @AgustinCoronel Si esto te parece complejo, deberias reveer tu concepto de complejo :p (esta consulta es muy simple). – gbianchi Feb 23 '19 at 05:33
  • puse el codigo que me mandaste pero sigue sin devolverme ningun valor – Agustin Coronel Feb 23 '19 at 16:18
  • @AgustinCoronel no conozco tus datos ni tengo forma de probarlos. Si no devuelve nada, pueden ser muchos los motivos y no tengo forma de probarlo. Si queres mas alla, arma un fiddle en algun lado para que podamos ayudarte mejor. – gbianchi Feb 23 '19 at 16:42
  • hola, osea quiero que me devuelva todos los clientes tengan o no una deudas, en caso de no tener deudas quiero que me devuelva el valor 0(cero) – Agustin Coronel Feb 23 '19 at 18:59
0

No estabas tan lejos de la forma que lo hiciste, pero la agrupacion y la sumatoria debe ir dentro de la tabla DeudaxProducto. Intenta esto:

SELECT c.nombre, IFNULL(dp.totaldeuda,0) FROM Clientes c
 LEFT JOIN Deudas d ON c.id=d.cliente
 INNER JOIN (SELECT iddeuda, sum(precio) totaldeuda from DeudaxProducto group by iddeuda) as dp
 ON d.id=dp.ideuda
  • 1
    Por cierto, si agregas el campo totalDeuda a la tabla de deudas como mencionaste, tendras que implementar un trigger para actualizar dicha tabla cada vez que agregues un producto en la tabla DeudaxProducto – Hernan Diaz Mar 15 '21 at 07:44
0

Primero, veamos de alcanzar el objetivo:

Para lograr tu objetivo, valiéndote de CTE's puedes escribir una consulta que:

  • Primero calcule el saldo total por cliente, para aquellos clientes que tienen una deuda
  • Luego tomar la tabla de clientes como tabla principal y hacer un left join con el cálculo realizado en el paso anterior. Esto es necesario para que el resultado final incluya a todos los clientes, tengan deuda o no.
  • Valerte de la función coalesce() para que los clientes sin deuda aparezcan con 0 y no con null

En código sería algo como:

with
Resumen as (
select Deudas.Cliente, sum(DeudaxProducto.precio) TotalDeuda
  from Deudas
       inner join DeudaxProducto on DeudaxProducto.ideuda = Deudas.id
 group by Deudas.Cliente
)
select Clientes.nombre, coalesce(Resumen.TotalDeuda, 0) TotalDeuda
  from Clientes
       left join Resumen on Resumen.Cliente = Clientes.id

Puedes conseguir exactamente el mismo resultado sin usar CTE's, pero en mi opinión el primero es más elegante y mantenible, mientras que su plan de ejecución, si no es idéntico, debe ser bastante similar.

Para hacer la consulta sin CTE's, la idea general se mantiene, necesitamos que la consulta nos devuelva todos los clientes, tengan deuda o no. Por ello, la tabla principal de la consulta debe ser clientes y se hará left join con el resto de las tablas. Mientras que, entre deuda y DeudaxProducto podemos mantener el inner join, siempre que hagamos primero este y al final el left join con Clientes.

Como es usual, un trozo de código es mejor que mil palabras:

select Clientes.id, Clientes.nombre, coalesce(sum(DeudaxProducto.precio), 0) TotalDeuda
  from Clientes
       left join Deudas
                 inner join DeudaxProducto on DeudaxProducto.ideuda = Deudas.id
              on Deudas.Cliente = Clientes.id
 group by Clientes.id, Clientes.nombre

La indentación de la consulta es intencional, si prestas atención, verás que la condición del left join no se aplica inmediatamente a la tabla Deudas, sino que se aplica al resultado del inner join entre Deudas y DeudaxProducto. Esto es porque he diseñado así la consulta, que me parece es semanticamente lo que buscas.

He incluido intencionalmente la columna id de Clientes en el resultado y en la cláusula group by, de otra forma, dos clientes con el mismo nombre, terminarían apareciendo una sola vez con ambas deudas sumadas.

Ahora la parte filosófica:

  1. Yo apoyo que no almacenes totales en tu modelo. Yo generalmente lo evito, pues generalmente son innecesarios. Lo son cuando los volúmenes de información o de consultas son grandes, o es muy complicado mantener indices que hagan óptimas las consultas que resumen la información.
  2. La consulta da el resultado que buscas, de acuerdo al modelo pero, a menos que un cliente nunca pueda pagar sus deudas, dudo que de la deuda real de un cliente.

Finalmente, escribí una respuesta incluso después de haber varias a tu pregunta, porque en mi humilde opinión, ninguna respondía la pregunta con claridad o precisión, pues fallan de alguna manera.

jachguate
  • 25,659
  • 7
  • 35
  • 61