Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Firebird e Interbase (https://www.clubdelphi.com/foros/forumdisplay.php?f=19)
-   -   Pivote: Sigo sin entenderlo... (https://www.clubdelphi.com/foros/showthread.php?t=94410)

Angel.Matilla 28-01-2020 18:21:16

Pivote: Sigo sin entenderlo...
 
Lo siento. Por más vueltas que le doy no acabo de entender bien como hacer este tipo de querys. Creo que he visto todos los hilos en los que se trata este tema; estuve viendo el código que puso fjcg02 en Trasnformadas, crosstab, pivot, referencias cruzadas ...

(Un apunte: El código del ejemplo da un error -607 Malformed string al ejecutarlo con SQL Manager, y no he sido capaz de ver dónde está)

A partir de esta tabla:


quiero montar un query; los campos son INTEGER. En principìo había pensado en una salida parecida a esta:

Para esta salida uso este query:
Código SQL [-]
WITH Pivote AS (SELECT Codigo, Votos2, Total FROM SYSDBA122540 WHERE Total > 0) 
SELECT A.Nombre, 
       CAST(100 * MAX(C.VOTOS2) AS DECIMAL(10 ,2))/CAST(MAX(C.Total) AS DECIMAL(10 ,2)) PorcC, 
       CAST(100 * MAX(D.VOTOS2) AS DECIMAL(10 ,2))/CAST(MAX(D.Total) AS DECIMAL(10 ,2)) PorcD, 
       CAST(100 * MAX(E.VOTOS2) AS DECIMAL(10 ,2))/CAST(MAX(E.Total) AS DECIMAL(10 ,2)) PorcE, 
       CAST(100 * MAX(F.VOTOS2) AS DECIMAL(10 ,2))/CAST(MAX(F.Total) AS DECIMAL(10 ,2)) PorcF 
  FROM Proceso A, SYSDBA122540 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 A.Nombre ORDER BY Nombre
Pero probablemente me sea más útil para lo que quiero otra en que los nombres de las columnas sean cada uno de los tipos (municipales, autonómicas, etc.) y en cada fila esté cada uno de lso resultados. Algo así:
Código:

Orden    Municipales  Autonómicas  Generales  Europeas
1          40,0009    39,2431      38,5656    49,0767
2          45,9111    46,9671      43,5847    51,1198 
3          37,0923    37,0738      23,5756    38,8671
4          35,5026    28,5302      21,4285    28,6433

y por más vueltas que doy no sé como montarlo para que me genere esta última salida. De verdad que no acabo de entender cómo montar estos querys.

mamcx 28-01-2020 19:30:58

Sigues sin entender porque no haces caso. Te he dicho que pivote con JOINS no es la manera. Hay te puse un link a un tutorial, si lo miraste?.

Reitero: Sin ayuda del motor, hacer esto REQUIERE HACER PASOS "manuales". TIENES que usar CASE para "filtrar" los datos de columnas junto con GROUP BY.

Te paso otro link que muestra los pasos:

https://modern-sql.com/use-case/pivot

(Ahi usan FILTER pero es lo mismo con CASE)

Angel.Matilla 28-01-2020 20:44:45

Cita:

Empezado por mamcx (Mensaje 535416)
Sigues sin entender porque no haces caso. Te he dicho que pivote con JOINS no es la manera. Hay te puse un link a un tutorial, si lo miraste?.

Reitero: Sin ayuda del motor, hacer esto REQUIERE HACER PASOS "manuales". TIENES que usar CASE para "filtrar" los datos de columnas junto con GROUP BY.

Te paso otro link que muestra los pasos:

https://modern-sql.com/use-case/pivot

(Ahi usan FILTER pero es lo mismo con CASE)

Gracias por la respuesta. Mañana la miraré con calma donde tengo el proyecto. No obstante el uso del JOIN lo justifica porque puede no haber datos en todos los códigos; sólo por eso. Y, en cualquier caso, tengo que montar el query de forma manual porque el usuario puede decidir cuantos resultados recupera; yo estoy poniendo cuatro porque eso es lo que uso para las pruebas, pero puede querer más o menos.

Angel.Matilla 29-01-2020 12:04:51

Lo siento. No soy capaz de encontrar la solución. Ante todo gracias a mamcx por los enlaces que ha puesto; me han aclarado algunas cosas, pero el problema es que todos los ejemplos que vienen son con funciones que agrupan (SUM, COUNT, etc.) y por lo tanto hace falta GROUP BY pero yo necesito las filas individuales. Tengo que pasar de esta tabla:

a esta otra:

entendiendo que la primera fila (3, 4, etc.) serían los nombres de las columnas. Me da lo mismo hacerlo con un pivote o como sugiere mamcx con CASE, pero no se me ocurre absolutamente ninguna forma. Lo más próximo que he logrado con:
Código SQL [-]
SELECT CASE WHEN A.Proceso = 3 THEN A.Votos2 END Europeas,
       CASE WHEN A.Proceso = 4 THEN A.Votos2 END Generales,
       CASE WHEN A.Proceso = 5 THEN A.Votos2 END Autonomicas,
       CASE WHEN A.Proceso = 6 THEN A.Votos2 END Municipales
  FROM SYSDBA122540 A
es esto:

que evidentemente no es lo que busco.

cloayza 29-01-2020 15:23:54

Estimado Angel.Matilla, me intereso tu problema y creo que hay una opción, por supuesto no será la única ni la mejor, pero es un comienzo.

Esta solución consta de dos partes:
1) Procedimiento almacenado: Que genera un correlativo dentro de cada "PROCESO"
Proceso Fila
3 1
3 2
3 3
3 4
4 1
4 2
4 3
...

Código SQL [-]
create procedure SP_PROCESO_FILAS
  returns (
    proceso integer,
    fila integer,
    codigo integer,
    votos2 integer,
    total integer)
as
declare variable OldProc integer;
begin
  
  OldProc=-1;
  fila=0;
  for select proceso, codigo, votos2, total
      from votos
      into : proceso,
           :codigo,
           :votos2,
           :total
  do
  begin
        if (OldProc<>proceso) then
        begin
            fila=0;
            OldProc=proceso;
        end

        fila=fila+1;
        suspend;
  end
end

2) Consulta que utiliza el procedimiento almacenado y genera la vista de los datos según el formato requerido.
Código SQL [-]
/*
Esta consulta genera los procesos en forma de columnas
utiliza sp_procesos_filas
*/
select a.fila,
       sum(iif(A.Proceso = 3, A.Votos2,0)) Europeas,
       sum(iif(A.Proceso = 4, A.Votos2,0)) Generales,
       sum(iif(A.Proceso = 5, A.Votos2,0)) Autonomicas,
       sum(iif(A.Proceso = 6, A.Votos2,0)) Municipales
from SP_PROCESO_FILAS a
group by a.fila


Espero te sirva, saludos cordiales

Angel.Matilla 29-01-2020 17:50:41

Cita:

Empezado por cloayza (Mensaje 535443)
Estimado Angel.Matilla, me intereso tu problema y creo que hay una opción, por supuesto no será la única ni la mejor, pero es un comienzo.

Esta solución consta de dos partes:

cloayza: ¡¡¡CHAPEAU!!! Es exactamente lo que me hace falta. Tendré que pulirlo porque la tabla de la que saco los datos se crea en tiempo de ejecución pero efectivamente es la saldia que me hacía falta. Te invito a unas ||-||

Angel.Matilla 29-01-2020 19:33:45

Cita:

Empezado por cloayza (Mensaje 535443)
Estimado Angel.Matilla, me intereso tu problema y creo que hay una opción, por supuesto no será la única ni la mejor, pero es un comienzo.

Me está ocurriendo una cosa curiosísima. Si ejecuto el código para crear el procedimiento desde SQL Manager no da ningún problema; se crea y me deja hacer el select posterior sin problema alguna. Si lo creo por código desde el proyecto me da un error de sintaxis en la línea into :proceso,:
Cita:

Dynamic SQL Error
SQL error code = -104
Token unknown - line 15, column 10
?
No lo entiendo ¿Cuál puede ser la razón?

Casimiro Notevi 29-01-2020 19:45:22

Cambia el nombre de esa variable, ponle por ejemplo vproceso, es que tienes un campo que se llama igual.

fjcg02 29-01-2020 21:50:18

Cita:

Empezado por Angel.Matilla (Mensaje 535441)
Lo siento. No soy capaz de encontrar la solución. Ante todo gracias a mamcx por los enlaces que ha puesto; me han aclarado algunas cosas, pero el problema es que todos los ejemplos que vienen son con funciones que agrupan (SUM, COUNT, etc.) y por lo tanto hace falta GROUP BY pero yo necesito las filas individuales. Tengo que pasar de esta tabla:

a esta otra:

entendiendo que la primera fila (3, 4, etc.) serían los nombres de las columnas. Me da lo mismo hacerlo con un pivote o como sugiere mamcx con CASE, pero no se me ocurre absolutamente ninguna forma. Lo más próximo que he logrado con:
Código SQL [-]
SELECT CASE WHEN A.Proceso = 3 THEN A.Votos2 END Europeas,
       CASE WHEN A.Proceso = 4 THEN A.Votos2 END Generales,
       CASE WHEN A.Proceso = 5 THEN A.Votos2 END Autonomicas,
       CASE WHEN A.Proceso = 6 THEN A.Votos2 END Municipales
  FROM SYSDBA122540 A
es esto:

que evidentemente no es lo que busco.

Hola, una cosa solamente... qué criterio utilizas para agrupar los valores por filas? No hay un orden determinado, podrían salirte ordenados los registros de cualquier manera dependiendo de lo que al motor le parezca. Es así? bajo mi punto de vista faltaría otra columna para agrupar los registros por filas.

Es más, ejecutar la cosulta 3 veces seguidas podría dar resultados diferentes.

Saludos

fjcg02 29-01-2020 21:54:45

Quiero decir que me parece más razonable que sea algo así

FECHA Europeas, Generales,
2015 100 200
2016 50 70
2018 200 175
...

por lo tanto te tiene que faltar algún campo. Y si es así, te falta el group by.

Código SQL [-]
SELECT
     Año, 
      CASE WHEN A.Proceso = 3 THEN A.Votos2 END Europeas,
       CASE WHEN A.Proceso = 4 THEN A.Votos2 END Generales,
       CASE WHEN A.Proceso = 5 THEN A.Votos2 END Autonomicas,
       CASE WHEN A.Proceso = 6 THEN A.Votos2 END Municipales
  FROM SYSDBA122540 A
GROUP BY AÑO

o

Código SQL [-]
SELECT
       PARTIDO, 
       CASE WHEN A.Proceso = 3 THEN A.Votos2 END Europeas,
       CASE WHEN A.Proceso = 4 THEN A.Votos2 END Generales,
       CASE WHEN A.Proceso = 5 THEN A.Votos2 END Autonomicas,
       CASE WHEN A.Proceso = 6 THEN A.Votos2 END Municipales
  FROM SYSDBA122540 A
GROUP BY PARTIDO

a ver si avanzas...


Saludos

Casimiro Notevi 29-01-2020 22:32:43

Con un campo fecha sale así, no sé si es lo que busca:



Angel.Matilla 30-01-2020 07:39:10

Cita:

Empezado por Casimiro Notevi (Mensaje 535446)
Cambia el nombre de esa variable, ponle por ejemplo vproceso, es que tienes un campo que se llama igual.

Gracias. No es la primera vez que me dices lo mismo.:(

Angel.Matilla 30-01-2020 10:00:25

Por partes. Antes que nada gracias a todos por la ayuda prestada y pediros disculpas por las molestias.
Cita:

Empezado por fjcg02 (Mensaje 535449)
Quiero decir que me parece más razonable que sea algo así

FECHA Europeas, Generales,
2015 100 200
2016 50 70
2018 200 175
...

por lo tanto te tiene que faltar algún campo. Y si es así, te falta el group by.

Efectivamente hay un campo más que no he dado cuenta de poner y que también se consulta; es un campo código que sirve de clave única. Y sobre lo del group by, con este código (o similar):
Código SQL [-]
SELECT Año, 
      CASE WHEN A.Proceso = 3 THEN A.Votos2 END Europeas,
       CASE WHEN A.Proceso = 4 THEN A.Votos2 END Generales,
       CASE WHEN A.Proceso = 5 THEN A.Votos2 END Autonomicas,
       CASE WHEN A.Proceso = 6 THEN A.Votos2 END Municipales
  FROM SYSDBA122540 A
GROUP BY AÑO
el SQL da un error:
Cita:

Invalid token.
Dynamic SQL Error.
SQL error code = -104.
Invalid expression in the select list (not contained in either an aggregate function or the GROUP BY clause).
Y es lógico porque no hay ningún campo que acumule (SUM, COUNT, etc.)
Cita:

Empezado por Casimiro Notevi (Mensaje 535451)
Con un campo fecha sale así, no sé si es lo que busca:

El campo fecha sería el más lógico pero el problema es que no todos los procesos tienen lugar en el mismo año; así en España en el año 2019 han habido dos elecciones generales.

Casimiro Notevi 30-01-2020 10:49:02

Cita:

Empezado por Angel.Matilla (Mensaje 535454)
El campo fecha sería el más lógico pero el problema es que no todos los procesos tienen lugar en el mismo año; así en España en el año 2019 han habido dos elecciones generales.

No es problema, era solo un ejemplo, las fechas pueden ser del mismo año: 20190501, 20190623, 20190625, etc.

ASAPLTDA 30-01-2020 13:51:45

Posible Solucion
 
NO SE SI ESTO TE SIRVA, PUEDE SER LENTO SI LA TABLA ORIGEN ES MUY GRANDE

SELECT
(SELECT COUNT(*) FROM INMOV where INCODTRA = '1') ENTRADAS,
(SELECT COUNT(*) FROM INMOV where INCODTRA = '2') REUBICACION,
(SELECT COUNT(*) FROM INMOV where INCODTRA = '3') SALIDAS
FROM RDB$DATABASE

RDB$DATABASE <--- TIENE UN SOLO REGISTRO

AHORA PUEDES HACER TAMBIEN
WITH TABLA AS(
SELECT
(SELECT COUNT(*) FROM INMOV where INCODTRA = '1') ENTRADAS,
(SELECT COUNT(*) FROM INMOV where INCODTRA = '2') REUBICACION,
(SELECT COUNT(*) FROM INMOV where INCODTRA = '3') SALIDAS
FROM RDB$DATABASE)
SELECT * FROM TABLA

Casimiro Notevi 30-01-2020 15:23:16

Cita:

Empezado por ASAPLTDA (Mensaje 535458)
NO SE SI ESTO TE SIRVA, PUEDE SER LENTO SI LA TABLA ORIGEN ES MUY GRANDE

Se ve que eres novato por aquí, escribiendo en mayúsculas y sin etiquetas de código ;););)
No te doy el enlace a la guía de estilo porque ya sabes dónde está :p


La franja horaria es GMT +2. Ahora son las 12:02:20.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi