Como Agrupar y/o ReAgrupar Datos en Consulta SQL
-
martes, 13 de octubre de 2009 23:38
Saludos Amigos del Foro.
La Consulta que me trae a este foro para su ayuda es la siguiente:
1.- Tengo una Tabla en SQL Server 2000, la cual tiene muchas columnas y que para mi consulta me interesan algunas, pero necesito una forma particular de traer la informacion de ella, tratare de explicarme con un ejemplo:
Tabla de Ejemplo 1
Fecha-------NumeroD----Serial--NumeroZ--RazonSocial----RIF-----------CompDesde--CompHasta------Monto
17-09-2009--FA-000197--xxxx---zzzz------Empres1, C.A.--J-000000000--800---------800--------------100.00
17-09-2009--FA-000198--xxxx---zzzz------Empres2, C.A.--J-000000000--801---------801--------------103.00
17-09-2009--FA-000199--xxxx---zzzz------Empres3, C.A.--J-000000000--802---------802--------------110.00
17-09-2009--FA-000200--xxxx---zzzz------Pedro Perez----V-000000000--803---------803--------------100.00
17-09-2009--FA-000201--xxxx---zzzz------Pedro Perez----V-000000000--804---------804--------------100.00
17-09-2009--FA-000202--xxxx---zzzz------Empres2, C.A.--J-000000000--805---------805--------------103.00
17-09-2009--FA-000203--xxxx---zzzz------Empres3, C.A.--J-000000000--806---------806--------------110.00
17-09-2009--FA-000204--xxxx---zzzz------Pedro Perez----V-000000000--807---------807--------------100.00
17-09-2009--FA-000205--xxxx---zzzz------Empres1, C.A.--J-000000000--808---------808--------------100.00
17-09-2009--FA-000206--xxxx---zzzz------Empres2, C.A.--J-000000000--809---------809--------------103.00
17-09-2009--FA-000207--xxxx---zzzz------Empres3, C.A.--J-000000000--810---------810--------------110.00
Tengo una columna que me define el tipo de cliente, si es juridico o Natural, con esto puedo agrupar los tipos de facturas, ya que al agrupar los datos debo obtener un resultado similar al que abajo les muestro: (Ojo este es el resultado que realmente quiero)
Ejemplo Resulatdo Tabla 2 (como quisiera que fuese)
Fecha-------NumeroD----Serial--NumeroZ--RazonSocial----RIF-----------CompDesde--CompHasta------Monto
17-09-2009--FA-000197--xxxx---zzzz------Empres1, C.A.--J-000000000--800---------800--------------100.00
17-09-2009--FA-000198--xxxx---zzzz------Empres2, C.A.--J-000000000--801---------801--------------103.00
17-09-2009--FA-000199--xxxx---zzzz------Empres3, C.A.--J-000000000--802---------802--------------110.00
17-09-2009--N/A---------xxxx---zzzz------N/A------------N/A-----------803---------804--------------200.00
17-09-2009--FA-000202--xxxx---zzzz------Empres2, C.A.--J-000000000--805---------805--------------103.00
17-09-2009--FA-000203--xxxx---zzzz------Empres3, C.A.--J-000000000--806---------806--------------110.00
17-09-2009--N/A---------xxxx---zzzz------N/A------------N/A-----------807---------807--------------100.00
17-09-2009--FA-000205--xxxx---zzzz------Empres1, C.A.--J-000000000--808---------808--------------100.00
17-09-2009--FA-000206--xxxx---zzzz------Empres2, C.A.--J-000000000--809---------809--------------103.00
17-09-2009--FA-000207--xxxx---zzzz------Empres3, C.A.--J-000000000--810---------810--------------110.00
Como ven aca, quisiera se agruparan todas las facturas de los clientes naturales, pero con la diferencia de que si entre dos juridicos existen facturas naturales, estan se deben agrupar solo en ese rango, o sea, entre la factura juridica anterior y la siguiente, no agrupar todas las facturas naturales y mostrarmelas al final o al principio. (ya que esto es lo que realmente obtengo con la conulta sql que he realizado)
A continuacion les detallo lo que realmente me devuelve la consulta de sql:
Resultado de la Consulta Tabla 3
Fecha-------NumeroD----Serial--NumeroZ--RazonSocial----RIF-----------CompDesde--CompHasta------Monto
17-09-2009--FA-000197--xxxx---zzzz------Empres1, C.A.--J-000000000--800---------800--------------100.00
17-09-2009--FA-000198--xxxx---zzzz------Empres2, C.A.--J-000000000--801---------801--------------103.00
17-09-2009--FA-000199--xxxx---zzzz------Empres3, C.A.--J-000000000--802---------802--------------110.00
17-09-2009--FA-000202--xxxx---zzzz------Empres2, C.A.--J-000000000--805---------805--------------103.00
17-09-2009--FA-000203--xxxx---zzzz------Empres3, C.A.--J-000000000--806---------806--------------110.00
17-09-2009--FA-000205--xxxx---zzzz------Empres1, C.A.--J-000000000--808---------808--------------100.00
17-09-2009--FA-000206--xxxx---zzzz------Empres2, C.A.--J-000000000--809---------809--------------103.00
17-09-2009--FA-000207--xxxx---zzzz------Empres3, C.A.--J-000000000--810---------810--------------110.00
17-09-2009--N/A---------xxxx---zzzz------N/A------------N/A-----------803---------807--------------300.00
Como ven me agrupa todas las que considere como clientes Naturales, para la consulta esto es lo correcto, pero para la practica desearia que de alguna manera pueda controlar los numeros de comprobantes para que si entre una factura juridica y la siguiente, hay varias facturas Naturales, este me agrupe solo las naturales entre este rango y vuelva a colocar la siguiente juridica, y no me coloque todas las Facturas Naturales al final de forma agrupadas, ya que me daria un numero de comprobante errado.
A continuacion muestro como es mi consulta SQL original, esta me funciona y me entrega los resultados a partir de la tabla original como lo señalo en la tabla de resultado 3
SELECT CONVERT(DATETIME, SUBSTRING(CONVERT(VARCHAR, SAFACT.FechaE, 120), 1, 10), 120) AS FechaE, CASE WHEN SAFACT.NumeroP IS NULL THEN CASE WHEN SAFACT.TipoFac = 'A' THEN 'FA-' ELSE 'NC-' END + SAFACT.NumeroD ELSE CASE WHEN SACLIE.TipoCli = 0 THEN CASE WHEN SAFACT.TipoFac = 'A' THEN 'FA-' ELSE 'NC-' END + SAFACT.NumeroD ELSE 'N/A' END END AS NumeroD, SAFACT.NumeroP AS Serial, CASE WHEN SAFACT.NumeroP IS NULL THEN NULL ELSE SAFACT.NumeroZ END AS NumeroZ, CASE WHEN SACLIE.TipoCli = 0 THEN SAFACT.Descrip ELSE 'Resumen diario de ventas' END AS Razon_Social, CASE WHEN SACLIE.TipoCli = 0 THEN SAFACT.ID3 ELSE 'N/A' END AS RIF, MIN(SAFACT.NumeroF) AS CompDesde, MAX(SAFACT.NumeroF) AS CompHasta, SUM(SAFACT.Signo * SAFACT.TExento) AS TExento, SUM(SAFACT.Signo * SAFACT.Monto) AS BaseImponible, SUM(SAFACT.Signo * SAFACT.MtoTax) AS Impuesto, SUM(SAFACT.Signo * (SAFACT.Monto + SAFACT.MtoTax - SAFACT.TExento)) AS MontoTotal FROM SAFACT LEFT OUTER JOIN SACLIE ON SAFACT.CodClie = SACLIE.CodClie WHERE (SAFACT.TipoFac IN ('A', 'B')) AND (YEAR(SAFACT.FechaE) = 2009) AND (MONTH(SAFACT.FechaE) = 9) AND (DAY(SAFACT.FechaE) = 17) GROUP BY CONVERT(DATETIME, SUBSTRING(CONVERT(VARCHAR, SAFACT.FechaE, 120), 1, 10), 120), SAFACT.NumeroP, SAFACT.NumeroZ, CASE WHEN SACLIE.TipoCli = 0 THEN SAFACT.Descrip ELSE 'Resumen diario de ventas' END, CASE WHEN SACLIE.TipoCli = 0 THEN SAFACT.ID3 ELSE 'N/A' END, CASE WHEN SAFACT.NumeroP IS NULL THEN CASE WHEN SAFACT.TipoFac = 'A' THEN 'FA-' ELSE 'NC-' END + SAFACT.NumeroD ELSE CASE WHEN SACLIE.TipoCli = 0 THEN CASE WHEN SAFACT.TipoFac = 'A' THEN 'FA-' ELSE 'NC-' END + SAFACT.NumeroD ELSE 'N/A' END ENDGracia a todos por tomarse el tiempo para Leerlo
Atentamente.
Franklin Gonzalez
Maturin - Edo. Monagas - Venezuela
Todas las respuestas
-
miércoles, 14 de octubre de 2009 7:32En un caso como este, yo personalmente preferiría procesar los datos mediante un bucle en el lado cliente en lugar de tratar de construir una única sentencia Sql que devuelva los resultados apetecidos. A no ser que tu tabla contenga muchísimos registros de personas naturales y muy pocos de jurídicas, la diferencia de tráfico del servidor al cliente va a ser insignificante, y el coste de procesamiento para agrupar las personas naturales en el código cliente también es despreciable, así que no vale la pena calentarse la cabeza para hacerlo en el servidor.
Alternativamente, si no tienes más remedio que hacerlo en el servidor, puedes usar una función con un cursor. Aunque habitualmente se desaconseja usar cursores, este es uno de los casos en que puede merecer la pena recurrir a ellos. -
miércoles, 14 de octubre de 2009 15:03Ok Alberto, Gracias por responder
Bueno te comento que estaba por implementar lo de los cursores, pero debido a que los registros de facturas van en aumento, no quiero que a futuro, este empieze a tornarse lento al aplicar el cursor y se me vean afectados el resto de los usuarios.
Por otro lado voy a optar por solo construir ya la vista con los datos filtrados y en el generador de reportes de la aplicacion buscarle la vuelta para que me agrupe los clientes naturales y que me los presente respetando el correlativo de los comprobantes.
Gracias mil por tu respuesta. -
miércoles, 14 de octubre de 2009 17:11Moderador
Hola.Voy a interntar aportarte otra idea, me resisto a pensar que no sea factible y asequible conseguir lo que buscas sin necesidad de recurrir a cursores. Para empezar, parece que el único problema es la ordenación de los registros (cuando te refieres a "agrupación", creo que estás refiriéndote a "poner cerca", no hablas de un "group by"). Comparando el conjunto de registros deseado y cómo se presentan, parece que existen campos, CompDesde y CompHasta, que permitirían obtener la ordenación deseada. Con eso, tu consulta podría ser la siguiente:SELECT CONVERT(DATETIME, SUBSTRING(CONVERT(VARCHAR, SAFACT.FechaE, 120), 1, 10), 120) AS FechaE, CASE WHEN SAFACT.NumeroP IS NULL THEN CASE WHEN SAFACT.TipoFac = 'A' THEN 'FA-' ELSE 'NC-' END + SAFACT.NumeroD ELSE CASE WHEN SACLIE.TipoCli = 0 THEN CASE WHEN SAFACT.TipoFac = 'A' THEN 'FA-' ELSE 'NC-' END + SAFACT.NumeroD ELSE 'N/A' END END AS NumeroD, SAFACT.NumeroP AS Serial, CASE WHEN SAFACT.NumeroP IS NULL THEN NULL ELSE SAFACT.NumeroZ END AS NumeroZ, CASE WHEN SACLIE.TipoCli = 0 THEN SAFACT.Descrip ELSE 'Resumen diario de ventas' END AS Razon_Social, CASE WHEN SACLIE.TipoCli = 0 THEN SAFACT.ID3 ELSE 'N/A' END AS RIF, MIN(SAFACT.NumeroF) AS CompDesde, MAX(SAFACT.NumeroF) AS CompHasta, SUM(SAFACT.Signo * SAFACT.TExento) AS TExento, SUM(SAFACT.Signo * SAFACT.Monto) AS BaseImponible, SUM(SAFACT.Signo * SAFACT.MtoTax) AS Impuesto, SUM(SAFACT.Signo * (SAFACT.Monto + SAFACT.MtoTax - SAFACT.TExento)) AS MontoTotal FROM SAFACT LEFT OUTER JOIN SACLIE ON SAFACT.CodClie = SACLIE.CodClie WHERE (SAFACT.TipoFac IN ('A', 'B')) AND (YEAR(SAFACT.FechaE) = 2009) AND (MONTH(SAFACT.FechaE) = 9) AND (DAY(SAFACT.FechaE) = 17) GROUP BY CONVERT(DATETIME, SUBSTRING(CONVERT(VARCHAR, SAFACT.FechaE, 120), 1, 10), 120), SAFACT.NumeroP, SAFACT.NumeroZ, CASE WHEN SACLIE.TipoCli = 0 THEN SAFACT.Descrip ELSE 'Resumen diario de ventas' END, CASE WHEN SACLIE.TipoCli = 0 THEN SAFACT.ID3 ELSE 'N/A' END, CASE WHEN SAFACT.NumeroP IS NULL THEN CASE WHEN SAFACT.TipoFac = 'A' THEN 'FA-' ELSE 'NC-' END + SAFACT.NumeroD ELSE CASE WHEN SACLIE.TipoCli = 0 THEN CASE WHEN SAFACT.TipoFac = 'A' THEN 'FA-' ELSE 'NC-' END + SAFACT.NumeroD ELSE 'N/A' END END order by CompDesdePor otra parte, deberías intentar no aplicar funciones a los campos, sino a las constantes, por ejemplo, cambiando esto:(YEAR(SAFACT.FechaE) = 2009) AND (MONTH(SAFACT.FechaE) = 9) AND (DAY(SAFACT.FechaE) = 17)
Por esto otro:(SAFACT.FechaE >= '20090917' and SAFACT.FechaE < '20090918'
La forma en que lo utilizas imposibilita la generación de un plan de ejecución que realice una búsqueda, tendrás recorridos, con un notable perjuicio en el rendimiento.Volviendo a lo esencial, si la consulta no resuelve tu caso, nos dices.
Alberto López Grande (Visita mi blog en http://qwalgrande.blogspot.es/)- Propuesto como respuesta qwalgrandeMVP, Moderator miércoles, 14 de octubre de 2009 17:11
- Marcado como respuesta Eduardo PorteschellerMicrosoft Community Contributor, Moderator jueves, 16 de diciembre de 2010 13:45
-
viernes, 16 de octubre de 2009 17:27Ok Alberto Gracias por responder y disculpa la tardanza por contestar.
El punto no se si difiero de ti con respecto a lo de orden con respecto a lo de Group By, es que realmente de acuerdo a lo planteado inicialmente en mi pregunta, lo que se quiere es algo asi: (voy a colocar un ejemplo un poco mas especifico)
Documento------Cliente----------Comprobante---Monto
Factura 1--------Juridico----------0801-------------100,00
Factura 2--------Juridico----------0802------------- 50,00
Factura 3--------Natural----------0803-------------150,00
Factura 4--------Natural----------0804-------------110,00
Factura 5--------Juridico----------0805-------------120,00
Factura 6--------Natural----------0806-------------125,00
Factura 7--------Natural----------0807------------- 85,00
Factura 8--------Juridico----------0808------------- 45,00
Factura 9--------Natural----------0809-------------100,00
Factura 10-------Natural----------0810-------------120,00
Factura 11-------Natural----------0811-------------220,00
Factura 12-------Natural----------0812-------------150,00
Factura 13-------Natural----------0813-------------120,00
Factura 14-------Natural----------0814-------------120,00
Factura 15-------Natural----------0815-------------120,00
Factura 16-------Natural----------0816-------------250,00
Factura 17-------Natural----------0817-------------120,00
Factura 18-------Natural----------0818-------------320,00
Factura 19-------Natural----------0819-------------120,00
Factura 20-------Natural----------0820-------------100,00
lo que se quiere es generar una vista en la cual se reflejen los valores de la siguiente forma:
Documento------Cliente----------CompDesde---CompHasta-----Monto
Factura 1--------Juridico----------0801-----------0801-------------100,00
Factura 2--------Juridico----------0802-----------0802------------- 50,00
N/A-----------N/A-----------0803---------0804----------260,00---> Aca agrupa las dos Factura (3 y 4) y suma sus Montos(150 y 110)
Factura 5--------Juridico----------0805-----------0805-------------120,00
N/A-----------N/A-----------0806---------0807----------210,00---> Aca agrupa las dos Factura (6 y 7) y suma sus Montos(125 y 85)
Factura 8--------Juridico----------0808-----------0809------------- 45,00
N/A-----------N/A-----------0809---------0820---------1860,00---> Aca agrupa el resto ya que son todas Naturales
Para ello cree una consulta SQL en la Cual me agrupara las factura por tipo de cliente, colocando los Valores N/A para aquellos campos en los que no me interesa la informacion del mismo.
Para el campo CompDesde y CompHasta use la Funcion Min() y Max() asociada al comprobante, con este logro de que el comprobante inicial y final en una agrupacion sea el rango que necesito, ahora bien cuan creo la consulta SQL, esta me devuelve lo Siguiente:
Factura 1--------Juridico----------0801-----------0801-------------100,00
Factura 2--------Juridico----------0802-----------0802------------- 50,00
Factura 5--------Juridico----------0805-----------0805-------------120,00
Factura 8--------Juridico----------0808-----------0809------------- 45,00
N/A-----------N/A-----------0803---------0820---------2330,00---> Aca agrupa todas las Facturas Naturales
Este es el problema que tengo y en el cual estoy quizas dando vueltas en un circulo, porque mi forma de hacerlo en una unica consulta, sin el uso de cursores, me oriente a que use Group By.....
Ahora bien necesito el resultado en una vista para llamarla de el generador de reportes de la aplicacion...
Si se pudiese hacer de otra manera, quizas con stored procedure, ya que he leido que cursores no es lo mas recomendado y sabiendo que la base de datos de donde saco la informacion va a ir en aumento, menos.
Gracias Mil por todas sus Atenciones......................

