A seguir pensando...

Área de diversión. Acertijos matemáticos, juegos de ingenio, música, cine. Porque no todo es programar!
Avatar de Usuario
Mauricio
Desarrollador de Clarion
Mensajes: 1125
Registrado: Dom Feb 06, 2011 9:34 am
Ubicación: España
Contactar:

A seguir pensando...

Mensaje por Mauricio » Dom Feb 20, 2011 7:24 pm

Sé que no es viernes, no recuerdo tampoco cuál fue el último número de A seguir pensando y este tampoco es un problema matemático. De hecho estuve a punto de inaugurar una nueva serie, PPP (Problemas para pensar) pero me pareció que teníamos que mantener el nombre que Daniel le había puesto.
Bien, de qué se trata esta vez? Es un problema de programación con algunas características. Primero, está involucrado SQL. Supongo que cualquier tipo de SQL pero en este caso es el de Microsoft. Segundo, el problema tiene muchas soluciones, la idea es encontrar la más práctica.

He aquí el problema: tenemos una tabla en SQL llamada Facturas. Entre sus campos hay un número de factura (único), una fecha, un código de cliente y un total de la factura. Podría tener más pero a nosotros nos interesan solo estos. La idea es generar un browse o listado de lo facturado por día. Fácil, no? Pero hay una pequeña trampa, si un día no se facturó nada, ese día debe aparecer en el browse/listado con importe 0.

Ej:
Nro. Factura Fecha Cliente TotalFacturado
1 01/02/2011 23 150
2 01/02/2011 17 100
3 03/02/2011 2 86
4 04/02/2011 5 200

Ahora queremos ver lo facturado entre el 01/02/2011 y el 07/02/2011, deberíamos tener algo así:
Fecha TotalFacturado
01/02/2011 250
02/02/2011 0
03/02/2011 86
04/02/2011 200
05/02/2011 0
06/02/2011 0
07/02/2011 0

La solución más fácil es ir agregando registros si no hay para esa fecha pero si el período es muy largo eso puede ser algo tedioso.
En un libro de SQL que estoy leyendo encontré una solución más elegante (al menos eso creo yo).
Qué proponen uds.?
Mauricio, básicamente usando Clarion 6.3
www.tdcsoftware.com y www.clarioneros.com/blog


Avatar de Usuario
DanielRuzo
Al nivel de RZ
Mensajes: 124
Registrado: Dom Feb 06, 2011 7:45 pm
Ubicación: Uruguay
Contactar:

Re: A seguir pensando...

Mensaje por DanielRuzo » Lun Feb 21, 2011 12:56 pm

No sé si será más elegante, pero voy a presentar una forma de hacerlo.

Primero vamos a mostrar cómo obtener una secuencia de fechas utilizando la recursividad. Como los datos después se los voy a agregar de la tabla Orders de la base Northtwind (que casi todos tenemos instalada) y los datos de esa tabla comienzan en julio de 1996, vamos a generar la secuencia de fechas de ese mes:

Código: Seleccionar todo

DECLARE @Fecha DATE;
SET @Fecha='1996-07-01';
WITH Fechador (Fecha) as
(
	SELECT @Fecha
	UNION ALL
	SELECT DATEADD(day,1,Fecha)
	FROM Fechador
	WHERE Fecha < '1996-07-31'
	)
	SELECT Fecha FROM Fechador
	GO
Ahora que tenemos la secuencia, le agregamos los datos de las órdenes correspondientes al mes mediante un simple JOIN y agrupando por fecha para totalizar:

Código: Seleccionar todo

DECLARE @Fecha DATE;
SET @Fecha='1996-07-01';
WITH Fechador (Fecha) AS
(
	SELECT @Fecha AS Fecha
	UNION ALL
	SELECT DATEADD(day,1,Fecha) FROM Fechador WHERE Fecha<'1996-07-31'
	)
	SELECT Fecha, SUM(Quantity*UnitPrice) AS Total FROM Fechador
	LEFT OUTER JOIN Orders ON Fecha=Orders.OrderDate
	LEFT OUTER JOIN OrderDetails ON OrderDetails.OrderID=Orders.OrderID
	GROUP BY Fecha
	GO
Con esta técnica, los días que no tienen órdenes salen con NULL (no con cero), pero es una aproximación. ¿Hay alguna otra forma?
Última edición por DanielRuzo el Mié Feb 23, 2011 1:56 pm, editado 3 veces en total.
¡Saludos!

Daniel Ruzo
www.amazingGUI.com

Yo creo en la reencarnación:
antes tenía una vida y ahora soy programador

Avatar de Usuario
Mauricio
Desarrollador de Clarion
Mensajes: 1125
Registrado: Dom Feb 06, 2011 9:34 am
Ubicación: España
Contactar:

Re: A seguir pensando...

Mensaje por Mauricio » Lun Feb 21, 2011 1:01 pm

Es "parecida" a la solución que tengo. No me animo a decir que la mía es mejor, tal vez la vea un poquitín más sencilla.
Vamos a ver si alguien aporta algo más y si no publico la mía que además puede utilizarse para otras cosas.
Mauricio, básicamente usando Clarion 6.3
www.tdcsoftware.com y www.clarioneros.com/blog

Avatar de Usuario
DanielRuzo
Al nivel de RZ
Mensajes: 124
Registrado: Dom Feb 06, 2011 7:45 pm
Ubicación: Uruguay
Contactar:

Re: A seguir pensando...

Mensaje por DanielRuzo » Lun Feb 21, 2011 1:26 pm

Bueno, se podría evitar la definición de la variable utilizada para tomar la primera fecha haciéndolo con un CAST en el primer SELECT, y se podría también utilizar UNION en lugar de un JOIN para evitar que salgan los NULL. Pero esperemos tu solución.
¡Saludos!

Daniel Ruzo
www.amazingGUI.com

Yo creo en la reencarnación:
antes tenía una vida y ahora soy programador

Avatar de Usuario
Mauricio
Desarrollador de Clarion
Mensajes: 1125
Registrado: Dom Feb 06, 2011 9:34 am
Ubicación: España
Contactar:

Re: A seguir pensando...

Mensaje por Mauricio » Lun Feb 21, 2011 1:32 pm

Te doy una idea. En la solución se usa una tabla auxiliar. De acuerdo al autor la tabla esa la usa en todas sus bases de datos así que la define en la base de datos Model. Lo bueno, como dije antes, es que la tabla esa tiene muchos otros usos.
Mauricio, básicamente usando Clarion 6.3
www.tdcsoftware.com y www.clarioneros.com/blog

Avatar de Usuario
DanielRuzo
Al nivel de RZ
Mensajes: 124
Registrado: Dom Feb 06, 2011 7:45 pm
Ubicación: Uruguay
Contactar:

Re: A seguir pensando...

Mensaje por DanielRuzo » Lun Feb 21, 2011 2:37 pm

Mmmmm...será cuestión de gustos, pero si usa una tabla auxiliar ya no me parece elegante. ¿Será por lo de la stupid table?

Pero esperemos... esperemos la solución.
¡Saludos!

Daniel Ruzo
www.amazingGUI.com

Yo creo en la reencarnación:
antes tenía una vida y ahora soy programador

Avatar de Usuario
DanielRuzo
Al nivel de RZ
Mensajes: 124
Registrado: Dom Feb 06, 2011 7:45 pm
Ubicación: Uruguay
Contactar:

Re: A seguir pensando...

Mensaje por DanielRuzo » Mié Feb 23, 2011 12:06 am

¿Y? ¿Para cuándo tu solución?
¡Saludos!

Daniel Ruzo
www.amazingGUI.com

Yo creo en la reencarnación:
antes tenía una vida y ahora soy programador

Avatar de Usuario
Mauricio
Desarrollador de Clarion
Mensajes: 1125
Registrado: Dom Feb 06, 2011 9:34 am
Ubicación: España
Contactar:

Re: A seguir pensando...

Mensaje por Mauricio » Mié Feb 23, 2011 10:30 am

Quería ver si alguien más se enganchaba pero parece que no, así que no me queda otra que publicar la solución. Aclaro de nuevo que no digo que es la mejor ni mucho menos.
Como dije antes, necesita una tabla auxiliar, la llamaremos Numeros. Esta tabla tiene una secuencia de, oh casualidad, números, del 1 al que nosotros querramos, puede ser 1.000.000 por ejemplo. La podemos crear en cualquier base de datos pero si lo hacemos en Model la tenemos disponible para cualquiera de nuestra base de datos. O podemos usar CREATE SYNONYM también.

Una vez creada la tabla, el resto es bastante parecido a lo que vos hiciste.

Código: Seleccionar todo

SELECT DATEADD(day, Numeros.n - 1, '20060101') AS FechaFactura,
  SUM(F.TotalFactura)
FROM Numeros
  LEFT OUTER JOIN Facturas AS F
    ON DATEADD(day, Numeros.n - 1, '20060101') = F.FechaFactura
WHERE Numeros.n <= DATEDIFF(day, '20060101', '20081231') + 1
GROUP BY DATEADD(day, Numeros.n - 1, '20060101')
ORDER BY FechaFactura;
Lo bueno de esta tabla es que se puede utilizar para muchas otras cosas, por ejemplo para encontrar el menor número faltante en una secuencia (suponete que tenés un código de artículo numérico e ingresaron el 1,2,3,6,7,8... etc.. La idea es encontrar el 4 en forma sencilla) y cosas así.
Mauricio, básicamente usando Clarion 6.3
www.tdcsoftware.com y www.clarioneros.com/blog

Avatar de Usuario
DanielRuzo
Al nivel de RZ
Mensajes: 124
Registrado: Dom Feb 06, 2011 7:45 pm
Ubicación: Uruguay
Contactar:

Re: A seguir pensando...

Mensaje por DanielRuzo » Mié Feb 23, 2011 3:02 pm

Acepto lo de que es más simple pero como dije me resulta más elegante lo del secuenciador. Reitero que es una cuestión de gustos, pero eso de tener una tabla que no representa nada me parece traído de los pelos (como lo de la tabla estúpida para usar de buffer en Clarion).

Si no querés repetir el secuenciador cada vez que lo usás se podría crear un Stored Procedure. Y en mi ejemplo utilicé un secuenciador de fechas pero perfectamente puede ser un secuenciador de enteros.

Gracias por el acertijo. Personalmente sé poquito de SQL y con esto me hiciste mover las neuronas.
¡Saludos!

Daniel Ruzo
www.amazingGUI.com

Yo creo en la reencarnación:
antes tenía una vida y ahora soy programador

Avatar de Usuario
Mauricio
Desarrollador de Clarion
Mensajes: 1125
Registrado: Dom Feb 06, 2011 9:34 am
Ubicación: España
Contactar:

Re: A seguir pensando...

Mensaje por Mauricio » Mié Feb 23, 2011 5:57 pm

Creo que lo interesante de esto es no solo las soluciones sino que ambas son del lado del motor. Una de las cosas más importantes que debemos hacer cuando programamos con SQL, sea con el lenguaje que sea, es aprender a resolver la mayor cantidad de los problemas en el motor. De esa forma vamos a generar menos tráfico y los programas serán más eficientes.
Mauricio, básicamente usando Clarion 6.3
www.tdcsoftware.com y www.clarioneros.com/blog


Responder

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 6 invitados