PDA

Ver la Versión Completa : Query con pivote: No termino de entenderlo


Angel.Matilla
15-01-2020, 12:16:33
Por más vueltas que le estoy dando no acabo de comprender bien como funciona. Vamos a ver si soy capaz de explicarme.

Tengo dos tablas:

Proceso
CODIGO INTEGER
NOMBRE VARCHAR(30)

SYSDBA134147
PROCESO INTEGER,
CODIGO INTEGER,
FECHA SMALLINT,
VOTOS2 INTEGER DEFAULT 0,
VOTOS3 INTEGER DEFAULT 0,
VOTOS16 INTEGER DEFAULT 0,
VOTOS17 INTEGER DEFAULT 0,
VOTOS22 INTEGER DEFAULT 0,
VOTOS_3 INTEGER DEFAULT 0,
VOTOS_1 INTEGER DEFAULT 0,
VOTOS_2 INTEGER DEFAULT 0,
TOTAL INTEGER DEFAULT 0,
CENSO INTEGER DEFAULT 0

Esta última se crea sobre la marcha cada vez que se accede a la opción de la aplicación. El campo Proceso de ésta última es el campo Código de la primera. Con un query obtengo esta salida:
https://i.ibb.co/fQWhsdf/Tabla.jpg
Y lo que necesito es una salida en la que aparezcan en un misma línea los resultados de cada tipo:
MUNICIPALES 32,6317 37,5252 32,1975 19,8322
AUTONÓMICAS... etc.
Sé que tiene que ser con el método de pivote, pero ninguna de las pruebas que he hecho me sale bien. Con este query:
WITH Pivote AS (SELECT Proceso, Codigo, CAST(100 * VOTOS2 AS NUMERIC(6, 2)) / CAST(TOTAL AS NUMERIC(6, 2)) Porc FROM SYSDBA134147)
SELECT DISTINCT A.Nombre, C.Porc PorcC, D.Porc PorcD, E.Porc PorcE, F.Porc PorcF
FROM Proceso A, SYSDBA134147 B
LEFT JOIN Pivote C ON B.Codigo = C.Codigo
LEFT JOIN Pivote D ON B.Codigo = D.Codigo
LEFT JOIN Pivote E ON B.Codigo = E.Codigo
LEFT JOIN Pivote F ON B.Codigo = F.Codigo
WHERE A.Codigo = (SELECT Codigo FROM Proceso WHERE Nombre = 'MUNICIPALES') AND A.Codigo = B.Proceso
lo que he obtenido es esto:
https://i.ibb.co/vhr36WB/Tabla.jpg
Es evidente que me falta alguna condición pero no soy capaz de ver cual es.

Casimiro Notevi
15-01-2020, 13:37:39
Mira este (https://www.clubdelphi.com/foros/showthread.php?t=94102&highlight=pivot) enlace, a ver si te sirve.

mamcx
15-01-2020, 14:24:29
Como te habia mencionado antes, hacer pivot en sql (si usa un motor sin cláusula pivot) NO SE PUEDE HACER DE FORMA DIRECTA. No hay manera de hacerlo con joins ni nada parecido.

Toca, semi-manualmente, transponer filas a columnas, que se hace con cláusulas CASE.

Mira los pasos:

https://mode.com/sql-tutorial/sql-pivot-table/

Angel.Matilla
15-01-2020, 19:17:17
Gracias por vuestras respuestas. Estuve dando vueltas a vuestras sugerencias hasta que recordé que en otro punto del proyecto estoy usando este método y casi, casi lo logro. He dejado el query así:
WITH Pivote AS (SELECT * FROM SYSDBA134147)
SELECT DISTINCT A.Nombre, C.Votos2 VotosC, D.Votos2 VotosD, E.Votos2 VotosE, F.Votos2 VotosF
FROM Proceso A, SYSDBA134147 B
LEFT JOIN Pivote C ON B.Codigo = C.Codigo AND C.Codigo = 25
LEFT JOIN Pivote D ON B.Codigo = D.Codigo AND D.Codigo = 29
LEFT JOIN Pivote E ON B.Codigo = E.Codigo AND E.Codigo = 33
LEFT JOIN Pivote F ON B.Codigo = F.Codigo AND F.Codigo = 40
WHERE A.Codigo = B.Proceso
ORDER BY B.Proceso, B.Codigo

Evidentemente estoy probando y faltan líneas en ese query. De todas formas, esta es la salida que obtengo:
https://i.ibb.co/VtfbH9n/Tabla.jpg

Sólo me falta que todas las celdas de cada tipo estén en una sola línea.

mamcx
15-01-2020, 20:29:59
Recalco:

No hay manera de hacerlo con joins ni nada parecido.

P.D: Esta es una limitación de la imperfecta implementación del sql del modelo relacional, pero no hay como arreglarla sin los pasos manuales requeridos...

fjcg02
15-01-2020, 21:53:06
prueba con esto:

WITH Pivote AS (SELECT * FROM SYSDBA134147)
SELECT A.Nombre, MAX(C.Votos2) VotosC, MAX(D.Votos2) VotosD, MAX(E.Votos2) VotosE, MAX(F.Votos2) VotosF
FROM Proceso A, SYSDBA134147 B
LEFT JOIN Pivote C ON B.Codigo = C.Codigo AND C.Codigo = 25
LEFT JOIN Pivote D ON B.Codigo = D.Codigo AND D.Codigo = 29
LEFT JOIN Pivote E ON B.Codigo = E.Codigo AND E.Codigo = 33
LEFT JOIN Pivote F ON B.Codigo = F.Codigo AND F.Codigo = 40
WHERE A.Codigo = B.Proceso
GROUP BY A.Nombre



A ver si te sale...

Saludos

mamcx
16-01-2020, 02:44:33
JOIN no es la solucion. los join crecen las filas, que es lo que menos se quiere aqui. Necesitan son aggregates (SUMs, GROUP By, ...).

Angel.Matilla
16-01-2020, 10:24:48
prueba con esto:

WITH Pivote AS (SELECT * FROM SYSDBA134147)
SELECT A.Nombre, MAX(C.Votos2) VotosC, MAX(D.Votos2) VotosD, MAX(E.Votos2) VotosE, MAX(F.Votos2) VotosF
...


A ver si te sale...

Saludos
En principio parece que sí. Gracias. A ver si me entero de una vez bien como demonios funciona esto del pivote,

JOIN no es la solucion. los join crecen las filas, que es lo que menos se quiere aqui. Necesitan son aggregates (SUMs, GROUP By, ...).
Efectivamente, necesito reducir el número de líneas.

Angel.Matilla
16-01-2020, 11:07:42
Vale. Después de unas cuantas pruebas el query me queda así:
WITH Pivote AS (SELECT * FROM SYSDBA134147)
SELECT DISTINCT A.Nombre,
MAX(C.Votos2) VotosC, MAX(D.Votos2) VotosD, MAX(E.Votos2) VotosE, MAX(F.Votos2) VotosF
FROM Proceso A, SYSDBA134147 B
LEFT JOIN Pivote C ON B.Codigo = C.Codigo AND C.Codigo IN (25,26,35,24)
LEFT JOIN Pivote D ON B.Codigo = D.Codigo AND D.Codigo IN (29,30,36,28)
LEFT JOIN Pivote E ON B.Codigo = E.Codigo AND E.Codigo IN (33,34,37,32)
LEFT JOIN Pivote F ON B.Codigo = F.Codigo AND F.Codigo IN (40,38,42,39)
WHERE A.Codigo = B.Proceso
GROUP BY 1
y la salida es esta, que es lo que buscaba:
https://i.ibb.co/b7kS97q/Tabla.jpg
Tal vez se pueda hacer mejor, pero no se me ocurre como. Muchas gracias a todos por la ayuda.

fjcg02
16-01-2020, 22:22:47
Creo que lo lias mucho...

Saca los datos en filas.
Nombre, Codigo, Total

Luego agrupas:
Campos de agrupacion
+
Casos
FROM
( CONSULTA)
GROUP BY Campos de Agrupacion

En tu caso:

SELECT
DATOS.NOMBRE <- Campos para agrupar por filas, pueden ser varios
SUM( CASE WHEN DATOS.CODIGO IN (1,2,3) THEN VOTOS ELSE 0 END ) as COLUMNA1,
SUM( CASE WHEN DATOS.CODIGO IN (4,5,6) THEN VOTOS ELSE 0 END ) as COLUMNA2,
ETC...
FROM
<- AQUI LA QUERY CON LOS DATOS EN FILAS. El resultado tiene alias DATOS, y devolvera las columnas NOMBRE, CODIGO y VOTOS o las que quieras... La query entre paréntesis
(
SELECT
A.Nombre, V.Codigo, V.Votos
FROM SYSDBA134147 V
INNER JOIN PROCESO A ON ( A.Campo1=V.Campo1) <- Desconozco tu modelo...
<- Aqui puede ir la where que limite los datos
) DATOS
<- FIN DE LA QUERY
Aqui puede ir una where sobre el reslutado DATOS
--AGRUPAS
GROUP BY
DATOS.NOMBRE


Y a probar...

Saludos

Angel.Matilla
17-01-2020, 10:31:08
Gracias por la respuesta. He estado probando pero o no he montado bien el query o no sale lo que me hace falta. He dejado así el query:
SELECT Datos.Nombre,
CASE WHEN Datos.Codigo IN (39, 32, 28, 24) THEN Votos2 ELSE 0 END Columna1,
CASE WHEN Datos.Codigo IN (42, 37, 36, 35) THEN Votos2 ELSE 0 END Columna2,
CASE WHEN Datos.Codigo IN (38, 34, 30, 26) THEN Votos2 ELSE 0 END Columna3,
CASE WHEN Datos.Codigo IN (40, 33, 29, 25) THEN Votos2 ELSE 0 END Columna4
FROM (SELECT A.Nombre, B.Codigo, B.Votos2
FROM SYSDBA194521 B
LEFT JOIN Proceso A ON A.Codigo = B.Proceso) Datos
Lo que necesito son los datos de cada uno de los casos y por ello no me hace falta el SUM; es irrelevante. Pero al ejecutar este query las salida es esta:
https://i.ibb.co/Bn6WSGf/Tabla.jpg
Así no me vale porque el resultado del query lo necesito para definir un TQRChart. Me hace falta que los resultados de cada tipo queden en una misma fila.

No obstante es una idea interesante que no se me había ocurrido.

Casimiro Notevi
17-01-2020, 12:11:32
¿Puedes poner la estructura de las tablas SOLAMENTE con los campos que necesitas y algunos datos para que podamos probar?
No pongas los campos que no son necesarios para que no nos confundamos.

bucanero
17-01-2020, 12:34:08
inténtalo con esta consulta:


SELECT
A.Nombre,
sum(Columna1) as columna1,
sum(Columna2) as columna2,
sum(Columna3) as columna3,
sum(Columna4) as columna4
FROM (SELECT
Proceso,
CASE WHEN Codigo IN (39, 32, 28, 24) THEN Votos2 ELSE 0 END Columna1,
CASE WHEN Codigo IN (42, 37, 36, 35) THEN Votos2 ELSE 0 END Columna2,
CASE WHEN Codigo IN (38, 34, 30, 26) THEN Votos2 ELSE 0 END Columna3,
CASE WHEN Codigo IN (40, 33, 29, 25) THEN Votos2 ELSE 0 END Columna4
FROM SYSDBA194521
group by Proceso, Codigo) B
LEFT JOIN Proceso A ON A.Codigo = B.Proceso
group by Proceso;

Angel.Matilla
17-01-2020, 12:36:44
Tabla Proceso
https://i.ibb.co/fvmVxPQ/Proceso.jpg (https://imgbb.com/)

Tabla SYSDBA194521
https://i.ibb.co/tcGbD25/SYSDBA194521.jpg (https://imgbb.com/)

El campo CODIGO de la tabla Proceso coincide con el campo PROCESO de la tabla SYSDBA194521.

bucanero
17-01-2020, 13:11:15
conforme a los datos que has publicado, y suponiendo que el primer dato de cada proceso va a la columna1, el segundo dato a la 2, y así sucesivamente, entonces la consulta debería de quedar de esta forma:

SELECT
A.Nombre,
sum(Columna1) as columna1,
sum(Columna2) as columna2,
sum(Columna3) as columna3,
sum(Columna4) as columna4
FROM (SELECT Proceso,
CASE WHEN Codigo IN (24, 35, 26, 25) THEN Votos2 ELSE 0 END as Columna1,
CASE WHEN Codigo IN (28, 36, 30, 29) THEN Votos2 ELSE 0 END as Columna2,
CASE WHEN Codigo IN (32, 37, 34, 33) THEN Votos2 ELSE 0 END as Columna3,
CASE WHEN Codigo IN (39, 42, 38, 40) THEN Votos2 ELSE 0 END as columna4
FROM SYSDBA194521
GROUP BY Proceso, Codigo) B
LEFT JOIN Proceso A ON A.Codigo = B.Proceso
group by Proceso;

La consulta en si, es la misma que he puesto anteriormente, solo cambian el valor del campo código para obtener el orden correcto.

fjcg02
17-01-2020, 14:59:47
Gracias por la respuesta. He estado probando pero o no he montado bien el query o no sale lo que me hace falta. He dejado así el query:
SELECT Datos.Nombre,
CASE WHEN Datos.Codigo IN (39, 32, 28, 24) THEN Votos2 ELSE 0 END Columna1,
CASE WHEN Datos.Codigo IN (42, 37, 36, 35) THEN Votos2 ELSE 0 END Columna2,
CASE WHEN Datos.Codigo IN (38, 34, 30, 26) THEN Votos2 ELSE 0 END Columna3,
CASE WHEN Datos.Codigo IN (40, 33, 29, 25) THEN Votos2 ELSE 0 END Columna4
FROM (SELECT A.Nombre, B.Codigo, B.Votos2
FROM SYSDBA194521 B
LEFT JOIN Proceso A ON A.Codigo = B.Proceso) Datos
Lo que necesito son los datos de cada uno de los casos y por ello no me hace falta el SUM; es irrelevante. Pero al ejecutar este query las salida es esta:
https://i.ibb.co/Bn6WSGf/Tabla.jpg
Así no me vale porque el resultado del query lo necesito para definir un TQRChart. Me hace falta que los resultados de cada tipo queden en una misma fila.

No obstante es una idea interesante que no se me había ocurrido.

Porque te falta el GROUP BY !!
todo lo que no vaya con función de garegado ( max, min, sum, avg, ..) debe ir en el group by.

Y utiliza lo que necesites, he puesto SUM como cualquier otra cosa. Y poniedo el group by necesitas SUM().

Saludos

Angel.Matilla
17-01-2020, 18:06:25
conforme a los datos que has publicado, y suponiendo que el primer dato de cada proceso va a la columna1, el segundo dato a la 2, y así sucesivamente, entonces la consulta debería de quedar de esta forma:
Vale. Este sí funciona. Muchas gracias a todos por la ayuda.