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)
-   -   SELECT con campos calculados y condicionados (https://www.clubdelphi.com/foros/showthread.php?t=93926)

Angel.Matilla 10-05-2019 13:03:26

SELECT con campos calculados y condicionados
 
¿Puede hacerse un query con un campo calculado en función de otro también calculado?
A ver si me explico mejor. Me interesa sacar un query de este estilo:
Código SQL [-]
SELECT A.Municipio, B.Nombre, F.Padron,
(SELECT COUNT(*) FROM Persona
WHERE CodPrv = A.CodPrv AND Junta = C.Codigo AND Situacion IN (SELECT Valor FROM Instalacion WHERE Etiqueta = 'Situacion'
AND Situacion = 1)) Afiliados, 
CASE WHEN F.Padron = 0 THEN 0 ELSE 100 * Afiliados / F.Padron END
FROM DatLoc A, HisElec F
WHERE A.Municipio = F.Municipio
AND A.Municipio = 1217
Pero al ejecutar el CASE, lógicamente, me da un error:
Cita:

Column does not belong to referenced table.
Dynamic SQL Error.
SQL error code = -206.
Column unknown.
AFILIADOS.
¿Como podría montarse el query para que ese CASE llegara a ejecutarse?

Casimiro Notevi 10-05-2019 13:26:56

¿Y ese campo calculado "afiliados" qué hace exactamente?

mRoman 10-05-2019 16:11:01

Cita:

Empezado por Angel.Matilla (Mensaje 531939)
¿Puede hacerse un query con un campo calculado en función de otro también calculado?
A ver si me explico mejor. Me interesa sacar un query de este estilo:
Código SQL [-]
SELECT A.Municipio, B.Nombre, F.Padron,
(SELECT COUNT(*) FROM Persona
WHERE CodPrv = A.CodPrv AND Junta = C.Codigo AND Situacion IN (SELECT Valor FROM Instalacion WHERE Etiqueta = 'Situacion'
AND Situacion = 1)) Afiliados, 
CASE WHEN F.Padron = 0 THEN 0 ELSE 100 * Afiliados / F.Padron END
FROM DatLoc A, HisElec F
WHERE A.Municipio = F.Municipio
AND A.Municipio = 1217
Pero al ejecutar el CASE, lógicamente, me da un error:

¿Como podría montarse el query para que ese CASE llegara a ejecutarse?

No le falta "AS" entre el último paréntesis y el campo "Afiliados"??
Código SQL [-]
WHERE CodPrv = A.CodPrv AND Junta = C.Codigo AND Situacion IN (SELECT Valor FROM Instalacion WHERE Etiqueta = 'Situacion'
AND Situacion = 1)) as Afiliados,

Y la parte del ELSE, no aplica la cuestión algebraica:
Código SQL [-]
100 * Afiliados / F.Padron
(100 * Afiliados ) / F.Padron 
/*En el camino de la informática olvide que los operadores aritméticos, llevan prioridad?...mmm...no recuerdo ya mis inicios!!!*/

Ahora que lo reescribo...veo que deberias agregar nuevamente toda la sentencia que calcula a "Afiliados", quedaría algo así:
Código SQL [-]
SELECT A.Municipio, B.Nombre, F.Padron,
(SELECT COUNT(*) FROM Persona
WHERE CodPrv = A.CodPrv AND Junta = C.Codigo AND Situacion IN (SELECT Valor FROM Instalacion WHERE Etiqueta = 'Situacion'
AND Situacion = 1)) Afiliados,
CASE WHEN F.Padron = 0 THEN 0 ELSE ( 100 * (SELECT COUNT(*) 
                                         FROM Persona
                                         WHERE CodPrv = A.CodPrv
                                             AND Junta = C.Codigo 
                                             AND Situacion IN (SELECT Valor 
                                                               FROM Instalacion 
                                                               WHERE Etiqueta = 'Situacion'
                                                                   AND Situacion = 1))
                                    ) / F.Padron END
FROM DatLoc A, HisElec F
WHERE A.Municipio = F.Municipio
AND A.Municipio = 1217

Prueba el Query y nos comentas....

Saludos.

manelb 10-05-2019 17:16:41

Para no tener que ejecutar la sentencia de "afiliados" dos veces, en algunas consultas similares, yo guardo el resultado en una variable.
El resultado será el mismo que con lo que te ha propuesto mRoman, pero en el caso de muchos registros, la diferencia de rendimiento puede ser significativa.

Un ejemplo en dos consultas de muestra que no tienen nigún sentido, solo ilustrar lo que quiero explicar:

Código SQL [-]
select codigo, 
      cast( RDB$get_CONTEXT('USER_TRANSACTION', 'CONTADOR_MBM') as integer)*1 as Total1, 
      cast( RDB$get_CONTEXT('USER_TRANSACTION', 'CONTADOR_MBM') as integer)*2 as Total2, 
      cast( RDB$get_CONTEXT('USER_TRANSACTION', 'CONTADOR_MBM') as integer)*3 as Total3, 
      cast( RDB$get_CONTEXT('USER_TRANSACTION', 'CONTADOR_MBM') as integer)*4 as Total4, 
      cast( RDB$get_CONTEXT('USER_TRANSACTION', 'CONTADOR_MBM') as integer)*5 as Total5, 
      RDB$SET_CONTEXT('USER_TRANSACTION', 'CONTADOR_MBM', ( select count(*) from tbl_clientes )) as SET_CONTEXT 
from tbl_clientes;


select codigo, 
      ( select count(*) from tbl_clientes )*1 as total1,
      ( select count(*) from tbl_clientes )*2 as total2,
      ( select count(*) from tbl_clientes )*3 as total3
from tbl_clientes;

La primera consulta siempre tarda lo mismo, aunque añadas más columnas calculadas.
En cambio la segunda versión, penaliza mucho por cada nueva columna.

Hay que tener en cuenta que las columnas se ejecutan de derecha a izquierda por lo que el RDB$SET_CONTEXT debe ser la columna más a la derecha para que las de la izquierda recuperen su valor actualizado.
Columnas situadas a la derecha del RDB$SET_CONTEXT recuperarian el valor de la consulta anterior.

Saludos

Angel.Matilla 10-05-2019 17:50:36

Cita:

Empezado por Casimiro Notevi (Mensaje 531942)
¿Y ese campo calculado "afiliados" qué hace exactamente?

Lo que se ve: Es un COUNT sobre otra tabla pero que me hace falta para el cálculo. Si no queda más remedio puedo hacer el cálculo después de ejecutar el query, pero me vendría bien hacerlo de una.
Cita:

Empezado por mRoman (Mensaje 531949)
No le falta "AS" entre el último paréntesis y el campo "Afiliados"??

No, no es imprescindible.
Cita:

Empezado por mRoman (Mensaje 531949)
En el camino de la informática olvide que los operadores aritméticos, llevan prioridad?...mmm...no recuerdo ya mis inicios!!!

Pero eso no es de informática, es de matemáticas. De las cuatro reglas básicas se ejecutan primero multiplicación y división de izquierda a derecha y luego sumas y restas en ese mismo orden; por lo tanto las dos estructuras que has puesto darían el mismo resultado.
Cita:

Empezado por mRoman (Mensaje 531949)
Ahora que lo reescribo...veo que deberias agregar nuevamente toda la sentencia que calcula a "Afiliados", quedaría algo así:

Ya, así ya sé que funciona pero así tengo hacer dos veces el SELECT COUNT, que consume recursos, y es lo que quiero evitar. Por eso intentaba hacerlo de la otra forma.
Cita:

Empezado por manelb (Mensaje 531950)
Para no tener que ejecutar la sentencia de "afiliados" dos veces, en algunas consultas similares, yo guardo el resultado en una variable.

Algo así es lo que buscaba pero no acabo de ver como encajar tu sugerencia en mi query. Al fin y al cabo es lo que estoy haciendo yo; tu pones ( select count(*) from tbl_clientes )*1 as total1 y la variable es total1. Yo pongo (SELECT COUNT [...]) Afiliados y la variable sería Afiliados. ¿Qué más me da si luego me dice que no sabe que es Afiliados?

manelb 10-05-2019 20:11:11

Cita:

Empezado por Angel.Matilla (Mensaje 531952)
tu pones ( select count(*) from tbl_clientes )*1 as total1 y la variable es total1. Yo pongo (SELECT COUNT [...]) Afiliados y la variable sería Afiliados. ¿Qué más me da si luego me dice que no sabe que es Afiliados?



No, la variable no es Total1.
Total1 es el nombre de la columna.

Yo utilizo variables en la primera de las dos consultas que he enviado, y el nombre de la variable es: 'CONTADOR_MBM'

Fíjate que RDB$SET_CONTEXT guarda el valor en la variable y RDB$GET_CONTEXT lo recupera.

Voy a intentar adaptar esto a tu consulta y te lo passo...

manelb 10-05-2019 20:20:49

Creo que debería estar bien, pero sin BD para probar....
Ya me dirás.

Código SQL [-]
SELECT A.Municipio, B.Nombre, F.Padron,
       cast( RDB$GET_CONTEXT('USER_TRANSACTION', 'AFILIADOS') as integer) AS Afiliados,
       CASE WHEN F.Padron = 0 THEN 0 ELSE 100 * cast( RDB$get_CONTEXT('USER_TRANSACTION', 'AFILIADOS') as integer) / F.Padron END,
       RDB$SET_CONTEXT('USER_TRANSACTION', 'AFILIADOS', (SELECT COUNT(*) 
                                                         FROM Persona 
                                                         WHERE CodPrv = A.CodPrv AND 
                                                               Junta = C.Codigo AND 
                                                               Situacion IN (SELECT Valor 
                                                                             FROM Instalacion 
                                                                             WHERE Etiqueta = 'Situacion' AND Situacion = 1)) ) as var_Afiliados 
FROM DatLoc A, HisElec F
WHERE A.Municipio = F.Municipio AND 
      A.Municipio = 1217

Saludos...

Casimiro Notevi 10-05-2019 20:35:48

En firebird no es necesario el "as", aunque se suele poner por compatibilidad con otros sistemas de BD.

mamcx 10-05-2019 21:07:58

El "truco" general es muy simple. Puedes insertar un select siempre donde hay un nombre de tabla:

Código SQL [-]
SELECT campo FROM
(SELECT 1 AS campo)a

Esto es lo mismo que hace un VIEW, solo que CREATE VIEW "guarda" ese SQL con un nombre.

Sabiendo esto, es solo de "partir" en 2 la sentencia: Crear un VIEW "anónimo" y luego jalar los datos:

Código SQL [-]

SELECT Afiliados .....
FROM 
(SELECT COUNT(*) AS Afiliados  FROM Persona JOIN ....

) Afiliado JOIN ....

Angel.Matilla 11-05-2019 11:56:14

Gracias por vuestra ayuda.
Cita:

Empezado por manelb (Mensaje 531956)
No, la variable no es Total1.
Total1 es el nombre de la columna.

Yo utilizo variables en la primera de las dos consultas que he enviado, y el nombre de la variable es: 'CONTADOR_MBM'

Fíjate que RDB$SET_CONTEXT guarda el valor en la variable y RDB$GET_CONTEXT lo recupera.

Sale justo lo que me hace falta. Estupendo.


La franja horaria es GMT +2. Ahora son las 09:05:32.

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