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)
-   -   Se puede Usar CASE WHEN, IF() O IIF() DENTRO DE LA CLAUSULA WHERE (https://www.clubdelphi.com/foros/showthread.php?t=91099)

ronalg 10-11-2016 23:50:36

Se puede Usar CASE WHEN, IF() O IIF() DENTRO DE LA CLAUSULA WHERE
 
Muy buenas:

tengo un procedimiento almadenado en firebird que me retorna un cojunto de datos, el procedimiento ya me funciona pero ahora necesito hacer que dicho procedimento "filtre" de acuerdo a un parametro que envio a ella.

Mi pregunta se puede hacer algo así en la sentencia WHERE
Código SQL [-]
select a.codigo, a.numero_cv, a.tipo_transaccion
from tb_compras_ventas a
where
   case
      when :cod_cliente>-1 then a.ref_cliente=:cod_cliente
      when :cod_cliente=-1 then --no hacer nada

talvez sea un disparate pero bueno, la idea es:

Si el parámetro cod_cliente es -1 (Que devuelva todos los registros de todos los clientes)
Si el parámetra cod_cliente es >1 (que devuelva todos los registros de ese cliente)

Mil gracias de antemano

ecfisa 11-11-2016 06:33:21

Hola
Cita:

Empezado por ronalg (Mensaje 510629)
...
talvez sea un disparate pero bueno, la idea es:

Si el parámetro cod_cliente es -1 (Que devuelva todos los registros de todos los clientes)
Si el parámetra cod_cliente es >1 (que devuelva todos los registros de ese cliente)

Basándome en los campos que mostras en tu código, podrías hacer:
Código SQL [-]
SET TERM ^ ;

CREATE OR ALTER PROCEDURE SP_TEST (
  COD_CLIENTE INTEGER )
RETURNS (
  R_CODIGO           INTEGER,
  R_NUMERO_CV        INTEGER,
  R_TIPO_TRANSACCION INTEGER )
AS
  DECLARE VARIABLE SENTENCIA VARCHAR(512);
BEGIN
  IF ( COD_CLIENTE = -1 ) THEN
    SENTENCIA = 'SELECT A.CODIGO, A.NUMERO_CV, A.TIPO_TRANSACCION '||
                'FROM TB_COMPRAS_VENTAS A';
  ELSE
    SENTENCIA = 'SELECT A.CODIGO, A.NUMERO_CV, A.TIPO_TRANSACCION '||
                'FROM TB_COMPRAS_VENTAS A' ||
                'WHERE A.REF_CLIENTE = ' || :COD_CLIENTE;

  FOR EXECUTE STATEMENT SENTENCIA
  INTO
    :R_CODIGO,
    :R_NUMERO_CV,
    :R_TIPO_TRANSACCION
  DO
    SUSPEND;
END^

SET TERM ; ^

Ejemplo de uso:
Código SQL [-]
SELECT R_CODIGO, R_NUMERO_CV, R_TIPO_TRANSACCION FROM SP_TEST(6)

Saludos :)

ronalg 11-11-2016 13:01:56

Mil gracias por la respueta
 
Es justamente lo que quiero evitar hacer, pues haría que el código de la consulta dentro del
Código SQL [-]
IF (cod_cliente = -1) then
se repita dos veces y como es una consulta larga y compleja me parece "desperdicio de código" si a eso le sumas que en realidad son dos campos por los cuales debo filtrar la consulta eso implica que el código se repetiría 4 veces.

Por eso estaba interesado en saber si es posible ejecutar partes de una sentencia WHERE en forma condicional, algo así como hacemos con código en Delphi, remplazar, agregar o eliminar una fila de la consulta antes de ejecutarla.

Espero que sea posible, si no la solución que me sugieres sería la única por el momento.

A seguir trabajando..... Y mil gracias (excelente tu ayuda)

ecfisa 11-11-2016 18:37:25

Hola.

Sinceramente no veo manera de hacer lo que consultas en el primer mensaje pero, dependiendo del formato de la sentencia SQL original, se podría optimizar un poco el código anterior del SP para evitar la repetición:
Código SQL [-]
SET TERM ^ ;

CREATE OR ALTER PROCEDURE SP_TEST (
  COD_CLIENTE INTEGER )
RETURNS (
  R_CODIGO           INTEGER,
  R_NUMERO_CV        INTEGER,
  R_TIPO_TRANSACCION INTEGER )
AS
  DECLARE VARIABLE SENTENCIA VARCHAR(512);
BEGIN
  SENTENCIA = 'SELECT A.CODIGO, A.NUMERO_CV, A.TIPO_TRANSACCION '||
              'FROM TB_COMPRAS_VENTAS A';

  IF ( COD_CLIENTE <> -1 ) THEN
    SENTENCIA = SENTENCIA || 'WHERE A.REF_CLIENTE = ' || :COD_CLIENTE;

  FOR EXECUTE STATEMENT SENTENCIA
  INTO
    :R_CODIGO,
    :R_NUMERO_CV,
    :R_TIPO_TRANSACCION
  DO
    SUSPEND;
END^

SET TERM ; ^
Eso siempre que la sentencia SQL original no sea muy compleja y lo permita...

Saludos :)

cloayza 11-11-2016 19:05:06

Te propongo lo siguiente

Código SQL [-]
select a.codigo, a.numero_cv, a.tipo_transaccion
from tb_compras_ventas a
where iif(:cod_cliente is null,1,iif(:cod_cliente=a.ref_cliente,1,0))=1

Saludos cordiales

ecfisa 11-11-2016 19:41:13

Cita:

Empezado por cloayza (Mensaje 510651)
Te propongo lo siguiente
...

^\||/ Ahí está la opción que no veía...

Saludos :)

ronalg 11-11-2016 19:43:27

Son unos genios
 
:eek:

Sigo intentando procesar que hace pero, funciona.

muchas gracias

mamcx 11-11-2016 19:59:46

Lo que hace es procesar de forma ineficiente la consulta, aunque logicamente correcto.

Ten en cuenta que SQL es un deficiente lenguaje que no se puede "recomponer" de forma arbitraria. Para eso estan los procedimientos almacenados.

ronalg 11-11-2016 20:06:16

Disculpa la neofita pregunta ¿Por qué ineficiente?
 
Cita:

Empezado por mamcx (Mensaje 510659)
Lo que hace es procesar de forma ineficiente la consulta, aunque logicamente correcto.

Ten en cuenta que SQL es un deficiente lenguaje que no se puede "recomponer" de forma arbitraria. Para eso estan los procedimientos almacenados.

La verdad no creo enteder que hace realmente el codigo

Cita:

select a.codigo, a.numero_cv, a.tipo_transaccion
from tb_compras_ventas a
where iif(:cod_cliente is null,1,iif(:cod_cliente=a.ref_cliente,1,0))=1
funciona a la prefección pero no logro entenderlo.

mamcx podrias darnos un luz sobre el asunto?

RONPABLO 11-11-2016 21:21:31

Código SQL [-]
select a.codigo, a.numero_cv, a.tipo_transaccion
from tb_compras_ventas a
where
  (:cod_cliente = -1 or a.ref_cliente=:cod_cliente)

el where que te entiendo es el anterior, no hace falta case ni nada más, coloco esos dos entre parentesis para que se solucione como una sola condición pensando que necesites luego agregar un and o algo más.

Respecto a un case en un where sería así:

Código SQL [-]
select a.codigo, a.numero_cv, a.tipo_transaccion
from tb_compras_ventas a
where case when :cod_cliente>-1 then a.ref_cliente  else -1 end = :cod_cliente

Osea, cuando el parametro :cod_cliente sea mayor a -1 manda el valor del campo llamado ref_cliente si no, mande un -1

mamcx 11-11-2016 21:32:53

Cita:

Empezado por ronalg (Mensaje 510664)
mamcx podrias darnos un luz sobre el asunto?


Debes hacer lo mismo cuando no entiendes el codigo, ejecutarlo paso a paso.

Esta es la parte clave
Código SQL [-]
where iif(:cod_cliente is null,1,iif(:cod_cliente=a.ref_cliente,1,0))=1

Cuando el motor lo recibe, lo sustituye por los valores. Que pasa si lo haces?

Código SQL [-]
where iif(null is null,1)

Lo cual se ejecuta POR CADA FILA DE LA TABLA. Es probable que FB pueda optimizar este caso (no estoy seguro: Hay que chequear el plan de ejecucion)

El problema es con el otro caso:

Código SQL [-]

where iif(false, iif(1=a.ref_cliente,1,0))=1
...
where  iif(True,1,0))=1
where  iif(False,1,0))=1
where  iif(False,1,0))=1
etc...

Estas anulando la posibilidad de usar el indice, porque tiene que chequear fila a fila.

Ves?

ronalg 11-11-2016 22:24:03

Muy agradecido mamcx

Ahora entiendo lo que hace el código y tienes razón, pero como dije, esa línea es una de varias en el where de mi consulta creo que vale la pena usarla pues me ahora mucho código, además de que es para el reporte de ventas por DIA o entre fechas o sea que primero recupero los códigos entre un rango de fechas; probare como sube o baja el rendimiento con muchos registros para decidir si usarlo así o en definitiva hacer mas largo el procedimiento almacenado.

RONPABLO muchas gracias lo probé y sorprendentemente (para mi) :eek: FUNCIONA ^\||/ tienes razón no era necesario pero estoy seguro que lo aprendido me será útil en otras ocasiones.


La franja horaria es GMT +2. Ahora son las 19:11:34.

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