A seguir pensando...
- Mauricio
- Desarrollador de Clarion
- Mensajes: 1125
- Registrado: Dom Feb 06, 2011 9:34 am
- Ubicación: España
- Contactar:
A seguir pensando...
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.?
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
www.tdcsoftware.com y www.clarioneros.com/blog
- DanielRuzo
- Al nivel de RZ
- Mensajes: 124
- Registrado: Dom Feb 06, 2011 7:45 pm
- Ubicación: Uruguay
- Contactar:
Re: A seguir pensando...
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:
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:
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?
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
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
Ú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
Daniel Ruzo
www.amazingGUI.com
Yo creo en la reencarnación:
antes tenía una vida y ahora soy programador
- Mauricio
- Desarrollador de Clarion
- Mensajes: 1125
- Registrado: Dom Feb 06, 2011 9:34 am
- Ubicación: España
- Contactar:
Re: A seguir pensando...
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.
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
www.tdcsoftware.com y www.clarioneros.com/blog
- DanielRuzo
- Al nivel de RZ
- Mensajes: 124
- Registrado: Dom Feb 06, 2011 7:45 pm
- Ubicación: Uruguay
- Contactar:
Re: A seguir pensando...
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
Daniel Ruzo
www.amazingGUI.com
Yo creo en la reencarnación:
antes tenía una vida y ahora soy programador
- Mauricio
- Desarrollador de Clarion
- Mensajes: 1125
- Registrado: Dom Feb 06, 2011 9:34 am
- Ubicación: España
- Contactar:
Re: A seguir pensando...
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
www.tdcsoftware.com y www.clarioneros.com/blog
- DanielRuzo
- Al nivel de RZ
- Mensajes: 124
- Registrado: Dom Feb 06, 2011 7:45 pm
- Ubicación: Uruguay
- Contactar:
Re: A seguir pensando...
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.
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
Daniel Ruzo
www.amazingGUI.com
Yo creo en la reencarnación:
antes tenía una vida y ahora soy programador
- DanielRuzo
- Al nivel de RZ
- Mensajes: 124
- Registrado: Dom Feb 06, 2011 7:45 pm
- Ubicación: Uruguay
- Contactar:
Re: A seguir pensando...
¿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
Daniel Ruzo
www.amazingGUI.com
Yo creo en la reencarnación:
antes tenía una vida y ahora soy programador
- Mauricio
- Desarrollador de Clarion
- Mensajes: 1125
- Registrado: Dom Feb 06, 2011 9:34 am
- Ubicación: España
- Contactar:
Re: A seguir pensando...
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.
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í.
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;
Mauricio, básicamente usando Clarion 6.3
www.tdcsoftware.com y www.clarioneros.com/blog
www.tdcsoftware.com y www.clarioneros.com/blog
- DanielRuzo
- Al nivel de RZ
- Mensajes: 124
- Registrado: Dom Feb 06, 2011 7:45 pm
- Ubicación: Uruguay
- Contactar:
Re: A seguir pensando...
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.
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
Daniel Ruzo
www.amazingGUI.com
Yo creo en la reencarnación:
antes tenía una vida y ahora soy programador
- Mauricio
- Desarrollador de Clarion
- Mensajes: 1125
- Registrado: Dom Feb 06, 2011 9:34 am
- Ubicación: España
- Contactar:
Re: A seguir pensando...
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
www.tdcsoftware.com y www.clarioneros.com/blog
¿Quién está conectado?
Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 13 invitados