Re: Consultas complicada

From: Francisco Olarte <folarte(at)peoplecall(dot)com>
To: ViBaSoft <vibasoft(at)gmail(dot)com>
Cc: Lista Postgres ES <pgsql-es-ayuda(at)postgresql(dot)org>
Subject: Re: Consultas complicada
Date: 2015-12-19 09:57:54
Message-ID: CA+bJJbwtQZMDGfRvPpepDA39Y=zp+ZtXsOM5xK7Aa6wm1ZB3Bw@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-es-ayuda

Buenos dias:

Una respuesta, larga, te voy desarrollando cosas.

On Fri, Dec 18, 2015 at 8:44 PM, ViBaSoft <vibasoft(at)gmail(dot)com> wrote:
> Quiero sacar el precio minimo, máximo, media por mes por columna y una
> general, en la general me sale bien pero por mes columna no me trae el
> minimo. Me trae 0(cero) siempre en la columna min_enero

Tu problema es el clasico de mal valor por defecto. Estas haciendo el
MIN de una columna de precios ( >=0 ) con un 0 cuando no es enero, con
lo que te sale 0:

> MIN(CASE WHEN extract(month from c.fecha_factura)=1 THEN cd.preciocompra
> ELSE 0 END) AS precio_min_enero,

En cuanto una fila de febrero entra el el case da 0, el minimo es 0.

Prueba a poner un numero enorme en el else, no se cual decirte ya que
no has puesto el esquema de la tabla pero siendo precios si pones
'ELSE 999999999999999999' te deberia valer.

Esa es la solucion clasica usada cuando calculas un minimo corrido en
un lenguaje clasico, inicializar el minimo al nuero mas largo que
puedas y si el valor leido es menor, actualizr ( min=99999999, if
curr<min then min=curr ) ).

En SQL puedes ademas hacer una mas astuta, ya que min filtra los
nulos, unos ejemplos, tu query actual con unos datos de pruebas:

s=> select min(case when mes=1 then precio else 0 end) from (
values(1,2.0),(1,3.0),(2,1.0),(2,4.0)) as datos(mes,precio); min
-----
0
(1 row)

La solucion con el 999999999:

cdrs=> select min(case when mes=1 then precio else 999999999 end) from
( values(1,2.0),(1,3.0),(2,1.0),(2,4.0)) as datos(mes,precio);
min
-----
2.0
(1 row)

Que tiene un problema si no hubiera datos de enero, por ejemplo para
el mes 3 pasa esto:

cdrs=> select min(case when mes=3 then precio else 999999999 end) from
( values(1,2.0),(1,3.0),(2,1.0),(2,4.0)) as datos(mes,precio);
min
-----------
999999999
(1 row)

Que es necesario controlar fuera de banda, si en lugar de 9999999
usas null te viene un null de resultado:

cdrs=> select min(case when mes=3 then precio else null end) from (
values(1,2.0),(1,3.0),(2,1.0),(2,4.0)) as datos(mes,precio);
min
-----

(1 row)

Que es mas facil de distinguir. Comprobamos que el caso del 1 sigue
funcioanado igual:

cdrs=> select min(case when mes=1 then precio else null end) from (
values(1,2.0),(1,3.0),(2,1.0),(2,4.0)) as datos(mes,precio);
min
-----
2.0
(1 row)

Y ademas el null vale tambien para max:

cdrs=> select max(case when mes=1 then precio else null end) from (
values(1,2.0),(1,3.0),(2,1.0),(2,4.0)) as datos(mes,precio);
max
-----
3.0
(1 row)

E incluso para avg, sum y count:

cdrs=> select avg(case when mes=1 then precio else null end) from (
values(1,2.0),(1,3.0),(2,1.0),(2,4.0)) as datos(mes,precio);
avg
--------------------
2.5000000000000000
(1 row)
cdrs=> select count(case when mes=1 then precio else null end) from (
values(1,2.0),(1,3.0),(2,1.0),(2,4.0)) as datos(mes,precio);
count
-------
2
(1 row)
cdrs=> select sum(case when mes=1 then precio else null end) from (
values(1,2.0),(1,3.0),(2,1.0),(2,4.0)) as datos(mes,precio);
sum
-----
5.0
(1 row)

Lo que te simplificaria bastante tu query, y lo deja, IMHO, mas claro,
ya que en SQL el NULL se ve enseguida como 'falta dato' o 'dato
desconocido' o 'no procede'. Incluso, aprovechando que el null
propaga, puedes hacerlo en dos partes, primero sacas una columna con
los datos de enero luego agregas. Como usas una version EOL no voy ni
a intentar hacerte una prueba real que no se si funcionara, no voy a
ir buscando documentaciones historicas, pero seria algo asi.

Tabla extendida:

SELECT
cd.codigo as codigo,
cd.cantidad as cant,
preciocompra,
(CASE WHEN extract(month from c.fecha_factura)=1 THEN cd.cantidad
ELSE NULL END) AS cant_enero,
(CASE WHEN extract(month from c.fecha_factura)=1 THEN
cd.preciocompra ELSE NULL END) AS precio_enero
FROM compras cd inner join compra c on cd.idcompras=c.idcompras
WHERE trim(cd.codigo)='90-131-00-00001' and c.fecha_factura>='2015-01-01'

Y luego puedes agregar con eso:

select
codigo,
sum(cant) as cant,
min(preciocompra) as min_precio,
max(preciocompra) as max_precio,
sum(cant_enero) as cant_enero,
min(precio_enero) as min_precio_enero,
max(precio_enero) as max_precio_enero,
sum(precio_enero*cant_enero)/sum(precio_enero) as media_enero
FROM (** LO DE ARRIBA**) as extendida
GROUP BY codigo.

******
> Trabajo con porstgres 9.0

Esa version esta EOL, yo que tu iria mirando de actualizar. De hecho
el query de ejemplo te lo hubiera puesto con WITH pero ni recuerdo si
lo soportaba en esa version.

Francisco Olarte.

-
Enviado a la lista de correo pgsql-es-ayuda (pgsql-es-ayuda(at)postgresql(dot)org)
Para cambiar tu suscripcin:
http://www.postgresql.org/mailpref/pgsql-es-ayuda

In response to

Responses

Browse pgsql-es-ayuda by date

  From Date Subject
Next Message Anthony Sotolongo 2015-12-19 10:54:31 Re: Consultas complicada
Previous Message ViBaSoft 2015-12-18 19:44:13 Consultas complicada