Duda con Isnull en SUM

04/11/2007 - 18:06 por Alexis | Informe spam
Solicito su ayuda para esto:

Tengo dos tablas
a) registros de compras
compras (numcompra, valor, fecha)

b)registros de pagos de esas compras
pagos (numcompra, numpago, valor)

Es una relacion opcional de 1 a N por Numcompra y quiero obtener los
balances de cada compra:

Numcompra, Comprado, Pagado

Tengo el siguiente select:

select numcompra, comprado=compras.valor, pagado=sum(pagos.valor)
from compras
left join pagos on compras.numcompra=pagos.numcompra
group by numcompra,compras.valor

Dado que es un Left Join, la pregunta es como debo usar ISNULL para que la
columna "Pagado" me de Cero cuando la compra no tenga ningun pago:

asi: ISNULL(sum(pagos.valor),0)
o asi: sum( ISNULL(pagos.valor,0) )
o asi: ISNULL(sum( ISNULL(pagos.valor,0) ), 0)
?


Gracias

Preguntas similare

Leer las respuestas

#6 Ricardo Passians
05/11/2007 - 12:11 | Informe spam
Mostrar la cita
Sujeto a confirmación (pues no puedo probarlo ahora) pero creo que el SUM()
no daría NULL en ese caso que dices, donde algunos valores fruto del LEFT
JOIN no existan. Si acaso puede dar una advertencia en algunos casos, si
mal no recuerdo ahora pero ciertamente creo que ignora los valores null al
sumar. Claro que si lo pone como sugieres (sum(isnull(valor,0)) el código es
mas seguro y se evitan las posibles advertencias.

Por otro lado, uno de los casos donde sí envolver el SUM con un ISNULL es
util es cuando no hay un un Group By.

Ejemplo para el caso que da Alexis:

select SUM(valor) from pagos where numcompraF

Si la compra # 46 no tiene ningun registro de pago, pues el resultado sería
un registro con un NULL!.
Por tanto aqui sería práctico hacerlo así:

select ISNULL(SUM(valor),0) from pagos where numcompraF

Sin embargo si hubiese algún group by, no es necesario el ISNULL
Si Alexis en vez de un LEFT JOIN, habria usado subconsultas, de esa forma
entonces sí habría requerido el ISNULL envolviendo al SUM:

select compras.numcompra, comprado=compras.valor,
pagado=(select ISNULL(SUM(pagos.valor),0) from pagos
where pagos.numcompra=compras.nomcompra)
from compras

Es de asumirse tambien que pagos.valor no contendrá NULLs por sí mismo.


Saludos

Ricardo Passians
#7 Carlos M. Calvelo
05/11/2007 - 12:29 | Informe spam
On 5 nov, 03:02, jeastman wrote:
Mostrar la cita
Hola,

Mira este ejemplo:
-
create table Tab (
TabId int not null primary key,
N int
)

insert into Tab (TabId,N) values (1,1)
insert into Tab (TabId,N) values (2,2)
insert into Tab (TabId,N) values (3,null)

select sum(N) from Tab

select sum(N) from Tab where TabId > 100

select sum(N) from tab where TabId = 3

drop table Tab


El primer select devuelve 3, pero debería ser null.
(4 + <algo_desconocido> debe ser <algo_desconocido>)

El segundo select devuelve null, pero debería ser 0.
(La suma de cero sumandos debe ser 0)

El tercer select devuelve null, como debe ser.


Si queremos interpretar el null como 0 (que no lo es!),
entonces se puede deducir de estos ejemplos que el
sum(isnull(N,0)) nunca tiene sentido.
En cuanto al isnull(sum(N),0) tenemos dos casos:
- Sumamos una selección con 0 registros:
Entonces isnull(sum(N),0) corrige el error de SQL.
- Sumamos una selección mas de 0 registros y todos
ellos tienen null en el campo a sumar:
Entonces isnull(sum(N),0) interpreta el resultado
null (que es correcto) como 0.

Conclusión: SQL no sabe sumar. :-)

Saludos,
Carlos
#8 jeastman
05/11/2007 - 14:56 | Informe spam
Hola Ricardo.

Es cierto, en un left join el sum no tiene problema hasta cierto punto,
me explico.

Si un renglon en tablaIzquierda tiene registros en tablaDerecha, no
retornará valor null para ese valor, caso contrario cuando no hay
conicidencias entre las dos tablas, veamos un ejemplo:


select i.codProducto,
sum( @impuesto * d.monto )
from inventario i left join detalleFacturas d on
d.numFactura = i.numFactura

En éste caso, dode necesitamos obtener la suma de todos los productos
aplicando una tasa, para los casos en que coincidan registros a ambos
lados del left se retornará un resultado, pero para aquellos casos donde
la tabla derrecha no tenga registros hay si hay un problema, d.monto
será null, la multiplicación dará null y el resultado de la suma dará
null y en esos casos uno lo que quieres un un valor numérico pero no un
null.


Yo prefiero hacerlo de la forma siguiente para evitarme ese problema, y
por que el isnull dentro del sum y no alrevez, por que solo envuelvo a
quien puede tener un valor null.

select i.codProducto,
sum( @impuesto * isnull( d.monto, 0 ) )
from inventario i left join detalleFacturas d on
d.numFactura = i.numFactura


Ahora en otro caso:

select i.codProducto,
sum( d.cantidad ) as cantidadFacturado,
sum( r.cantidad ) as cantidadDevuelto,
sum( d.cantidad - r.cantidad ) as cantidadEfectivaVentas
from inventario i inner join detalleFactura d on
d.codProducto = i.codProducto
left join (select codProducto,
sum( cantidad ) as cantidad
from reversoVenta r
group by codProducto ) as r on
r.codProducto = i.codProducto
group by i.codProducto

Ésto es un ejemplo simplificado

En éste caso, si para algún producto no hay devoluciones en venta tanto
cantidadDevuelto como cantidadEfectivaVenta retornará null.

Para ello es recomendable hacerlo de la forma siguiente:

select i.codProducto,
sum( d.cantidad ) as cantidadFacturado,
sum( isnull( r.cantidad, 0 ) ) as cantidadDevuelto,
sum( d.cantidad - isnull( r.cantidad, 0 ) ) as cantidadEfectivaVentas
from inventario i inner join detalleFactura d on
d.codProducto = i.codProducto
left join (select codProducto,
sum( cantidad ) as cantidad
from reversoVenta r
group by codProducto ) as r on
r.codProducto = i.codProducto
group by i.codProducto

Ahora, observemos que el isnull envuelve a la columna que pertenece a la
subconsulta que está a la parte derecha del left join y no del sum,
¿porqué, si se envuelve el sum y uno de los valores de r.cantidad es
null el sum será null y el resultado será cero (0), cual en realidad (en
el caso de cantidadEfectivaVentas) deve retornar el valor de la suma de
d.cantidad.

No se si logro explicarme, pero se puede probar lo que se muestra, si
existe alguna duda por favor indicarlo.


Lo que dice el amigo Ricardo es cierto en el ejemplo que pasé
inicialmente, si se cumple la condición entre las dos tablas retorna un
valor, pero, en caso contrario se retornará es un NULL y no un cero (0).

Saludos.


Ricardo Passians escribió:
Mostrar la cita
#9 jeastman
05/11/2007 - 15:05 | Informe spam
Hola Carlos.

Cuando creo tablas todos los campos son NOT NULL, por ello nunca me ha
pasado lo que demuestras en la consulta 1 y 2, acabo de probarlo y cual
fue mi sopresa!!!, el resultado no fue el esperado (por mi claro).

Gracias a ese comentario acabo de agregar algo más a mi conocimientos.

Saludos.

Carlos M. Calvelo escribió:
Mostrar la cita
#10 Alejandro Mesa
05/11/2007 - 15:44 | Informe spam
Hola Jorge,

Mostrar la cita
Las funciones de grupo no toman en cuenta valores NULL. Eso lo puedes ver en
la siguiente sentencia:

select SUM(CAST(NULL AS INT))
GO

Warning: Null value is eliminated by an aggregate or other SET operation.

(1 row(s) affected)

Cuando se usa una funcion de grupo, la sentencia debe devolver algo, pero si
no hay nada que agregar, entonces devuelve NULL.

SELECT SUM(c1)
FROM (SELECT CAST(NULL AS INT) WHERE 0 = 1) AS t1(c1)
GO

USE Northwind
GO

SELECT SUM(Quantity)
FROM dbo.[Order Details]
WHERE 0 = 1
GO

Asi que respondiendo la pregunta de Alexis, en tu caso podemos usar ambos
procedimientos, ISNULL(SUM(pagos.valor)) o SUM(ISNULL(pagos.valor)), dado que
el operador de union que usas, creara una fila de pago fictisia para las
compras que no tienen pago asociado.

SELECT
compras.numcompra,
SUM(compras.valor) AS comprado,
SUM(pagos.valor) AS pagado
FROM
(
SELECT 1 AS numcompra, 4 AS [valor]
UNION ALL
SELECT 2 AS numcompra, 17 AS [valor]
) AS compras
LEFT JOIN
(
SELECT 1 AS numcompra, 4 AS [valor]
) AS pagos
ON compras.numcompra = pagos.numcompra
GROUP BY
compras.numcompra

SELECT
compras.numcompra,
SUM(compras.valor) AS comprado,
ISNULL(SUM(pagos.valor), 0) AS pagado
FROM
(
SELECT 1 AS numcompra, 4 AS [valor]
UNION ALL
SELECT 2 AS numcompra, 17 AS [valor]
) AS compras
LEFT JOIN
(
SELECT 1 AS numcompra, 4 AS [valor]
) AS pagos
ON compras.numcompra = pagos.numcompra
GROUP BY
compras.numcompra

SELECT
compras.numcompra,
SUM(compras.valor) AS comprado,
SUM(ISNULL(pagos.valor, 0)) AS pagado
FROM
(
SELECT 1 AS numcompra, 4 AS [valor]
UNION ALL
SELECT 2 AS numcompra, 17 AS [valor]
) AS compras
LEFT JOIN
(
SELECT 1 AS numcompra, 4 AS [valor]
) AS pagos
ON compras.numcompra = pagos.numcompra
GROUP BY
compras.numcompra


AMB

"jeastman" wrote:

Mostrar la cita
Ads by Google
Search Busqueda sugerida