Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Conexión con bases de datos (https://www.clubdelphi.com/foros/forumdisplay.php?f=2)
-   -   Parámetro VarChar de búsqueda, ¿es válido aumentar su tamaño para evitar error? (https://www.clubdelphi.com/foros/showthread.php?t=61673)

Al González 17-11-2008 22:45:47

Parámetro VarChar de búsqueda, ¿es válido aumentar su tamaño para evitar error?
 
¡Hola!

Supongamos que tengo un SP (procedimiento almacenado) donde uno de sus parámetros es usado como valor a buscar dentro de una tabla. El parámetro, llamado P, está definido como VarChar (10), ya que el campo sobre el que busca el valor tiene la misma definición.

Dentro del SP, la cláusula Where de la sentencia SQL es:
Código SQL [-]
Where Campo Containing :P
(el operador usado también podría ser "=" u otros, en lugar de Containing)

Si ejecuto el procedimiento, dándole un valor de '1234567890A' para ese parámetro, el servidor, me arrojará el típico error "arithmetic exception, numeric overflow, or string truncation". Ya que el valor dado tiene once caracteres cuando el parámetro está definido con un tamaño de 10. Esto ocurre en Firebird, pero publico mi pregunta en esta sección por aplicarse, seguramente, a otras bases de datos también.

Entiendo que en estos casos se aconseja revisar previamente el valor dado para no sobrepasar la longitud máxima definida por el parámetro. Pero, a manera de prueba, hice otra cosa: aumenté el tamaño definido del parámetro a 11.

Resultado: el servidor ya no arroja ningún error, y el SP trabaja como se espera (por supuesto, no encuentra valores de once o más caracteres en un campo VarChar (10)). Probado en IB Expert con Firebird 1.5.

No estoy intentando asignar el valor dado a ningún campo, solamente lo uso para comparar, por lo que esta prueba que hice podría ser una solución alternativa en casos similares al mío. Resulta que el famoso valor dado es una cadena de caracteres que el usuario puede teclear dentro de un cuadro de texto para buscar dicho valor sobre diferentes campos. Y la aplicación por sí misma no conoce con precisión con qué parámetro(s) se va a buscar (aunque puede intervenir en ello, agregando algo de programación) ya que eso es determinado por otra capa del framework que utilizo.

Con esto he descubierto que si aumento, digamos, a 50, el tamaño de estos parámetros en todos los SPs de búsqueda. Ya sólo tendría que limitar la entrada en pantalla a un máximo de 50 caracteres para todos esos cuadros de texto, sin preocuparme más de cuál es el tamaño específico de cada campo donde se busca.

Mi pregunta es si, al hacer esto, ¿no estaría menguando significativamente el rendimiento del servidor a la hora de ejecutar esos SPs de búsqueda?

Quiero pensar que cuando Firebird encuentra una condición como
Código SQL [-]
Where Campo Containing :P
, y el valor de ese parámetro tiene una longitud mayor a la del campo, automáticamente descarta la fila que está comparando.

Comprendo que podría ser preferible revisar la longitud del valor antes de ejecutar el SP, para evitar que el servidor ejecute código inútil. Y quizá con esto esté ya contestándome respecto a lo que debo hacer. Pero viéndolo desde otro punto de vista, el procesamiento inútil del servidor podría ser prácticamente el mismo si buscamos un valor de longitud correcta pero que no existe en la tabla.

Quizá el argumento que termina de convencerme es: si el programa puede evitarle trabajo innecesario al servidor (sin causarle más trabajo), que lo evite. Creo que esta es una de esas cosas que debe hacer la "capa intermedia". La interfaz de usuario no tiene por qué validarlo puesto que carece de gran detalle de conocimiento respecto a los metadatos, y el servidor no debería hacer un procesamiento inútil que puede ser "autorizado" antes por alguien que conozca un poco más esos metadatos: la capa intermedia.

Entonces en la capa intermedia, en la parte del framework de clases donde concentro validaciones similares, agregaré una más para impedir la consulta al servidor cuando el valor String dado sobrepase el tamaño del parámetro VarChar en turno.

Este mensaje ha sido más que nada un pensamiento en voz alta, al más puro estilo Delphius :p (un abrazo, amigo ;)). Pero me gustaría conocer sus opiniones respecto a este tipo de problemas.

Saludos.

Al González. :)

sitrico 17-11-2008 23:29:37

Viendo el tema y reflexionando un poco creo que ni tienes razón ni estas equivocado sino todo lo contrario.:eek:

Bueno a lo que importa. Yo siempre limito los Tedit para char y varchar a la longitud máxima del campo en la BDD.

Código Delphi [-]
edit.MaxLength := FieldByname('Campo').Size;

Pero si tu consulta busca en varios campos:

Código SQL [-]
Where Campo1 Containing :P or Campo2 Containing :P

es OBLIGATORIO definir la longitud del campo más grande.

En lo personal pienso que no hay problema de definir un largo mayor, puede ser conveniente si piensas que el campo puede "crecer" más adelante. Pero no te recomendaría hacerlo como norma.

Saludos.

Luis M. 18-11-2008 11:03:09

Saludos.
Siempre es mejor utilizar la entrada de parámetros un poquito más grande
que la longitud del campo.

Si el campo es VarChar(10), el parámetro de entrada VarChar(10), cuando
le pases un valor con 10 dígitos, a este tienes que sumarle dos más por las
comillas.

Parámetro entrada: '1234567890' -> longitud 12, no 10.
Espero se me entienda.

Un saludo.

Al González 18-11-2008 17:11:59

Cita:

Empezado por Luis M. (Mensaje 326586)
....Si el campo es VarChar(10), el parámetro de entrada VarChar(10), cuando
le pases un valor con 10 dígitos, a este tienes que sumarle dos más por las
comillas.

Parámetro entrada: '1234567890' -> longitud 12, no 10....

Disculpa Luis, pero no creo que haya que reservar en un parámetro espacios para las comillas, puesto que el parámetro es declarado como cualquier variable y las comillas delimitadoras nunca son parte del valor de una cadena de caracteres. Las comillas se utilizan para que los compiladores o mecanismos de análisis sintáctico ("parsers") comprendan el dato proporcionado cuando se escribe de forma literal, pero no constituyen parte integral de la cadena de caracteres.

Creí oportuno precisar eso.

De todas formas, gracias por el comentario.

Al. :)

Luis M. 18-11-2008 17:38:44

Hola Al.
Tienes razón en lo que comentas.
Pero curiosamente a mi me pasaba lo siguiente:
Declaro este SP:
Código Delphi [-]
SET TERM ^ ;

CREATE OR ALTER PROCEDURE ULTIMO_RECIBO (
    anyo varchar(2))
returns (
    ultimo varchar(10))
as
begin
  /* Procedure Text */
  EXECUTE STATEMENT 'SELECT max(NUMERO) FROM RECIBOS where ANYO = '||:anyo
  INTO :ultimo;
  suspend;
end^

SET TERM ; ^

GRANT EXECUTE ON PROCEDURE ULTIMO_RECIBO TO SYSDBA;

Desde Delphi lo llamo así:
Código Delphi [-]
//--- Introduce último recibo ---
  SP.StoredProcName := 'ULTIMO_RECIBO';
  SP.Prepare;
  try
    SP.Params.ParamValues['ANYO'] := QuotedStr(RecibosANYO.AsString);
    SP.ExecProc;
    cText := IntToStr(SP.ParamByName('ULTIMO').AsInteger + 1);
    RecibosNUMERO.AsString := Copy('00000', 1, 5 - Length(cText)) + cText;
  finally
    SP.UnPrepare;
  end;
RecibosANYO es VarChar(2).
Al ejecutarlo me da el error que describes.
Si cambio en el SP
Código Delphi [-]
CREATE OR ALTER PROCEDURE ULTIMO_RECIBO (
    anyo varchar(2))
por:
Código Delphi [-]
CREATE OR ALTER PROCEDURE ULTIMO_RECIBO (
    anyo varchar(10))
Funciona bien.
A eso me refería.
Un saludo y encantado de leerte.

maeyanes 18-11-2008 19:41:47

Hola...

Luis M., ¿por qué haces tu procedimiento de esa forma?

Eso que tu haces lo puedes lograr así:

Código SQL [-]
SET TERM ^ ;

CREATE OR ALTER PROCEDURE ULTIMO_RECIBO (
    anyo varchar(2))
returns (
    ultimo integer)
as
begin
  /* Procedure Text */
  SELECT max(NUMERO) FROM RECIBOS where ANYO = :anyo
  INTO :ultimo;
  suspend;
end^

SET TERM ; ^

GRANT EXECUTE ON PROCEDURE ULTIMO_RECIBO TO SYSDBA;

Y no vas a recibir el error que mencionas...


Saludos...

Lepe 18-11-2008 20:02:34

No quería entrometerme en el hilo, pero ¡¡ ya estoy !!

Intuyo que el error viene porque el año son 4 dígitos. Tú quizás le pasas 2 dígitos al procedimiento almacenado, pero él quizás devuelva 4 letras y el procedimiento de salida sólo tenía 2 espacios.

Quizás estoy suponiendo mucho... no lo sé, entran en juego muchas variables y no está todo dicho:
- declaración de campos
- valores que tiene en la BBDD
- lo que se le pasa desde delphi
- lo que interpreta el SP (puede ser aquí la traducción de 2 dígitos a 4 para el campo numero)
- lo que devuelve
- etc.

Saludos

Luis M. 18-11-2008 20:38:20

Hola.
Cita:

Empezado por Lepe (Mensaje 326673)
No quería entrometerme en el hilo, pero ¡¡ ya estoy !!

Ni mucho menos Lepe, todo lo contrario, es un honor.
Te comento, el año son 2 dígitos, capturo solo los dos últimos.
Tengo algunos SP hechos como lo comentas en tú post y funcionan bien siempre y cuando no les pase parámetros; En el momento que le paso algún parámetro, tengo que implementarlo con EXECUTE STATEMENT, si no, no funcionan.
También comento que solo funciona con EXECUTE STATEMENT cuando hay
comandos como SUM,MAX, etc.
Este funciona perfectamente sín parámetros:
Código Delphi [-]
SET TERM ^ ;

CREATE OR ALTER PROCEDURE ULTIMO_CARNET 
returns (
    ultimo varchar(10))
as
begin
  /* Procedure Text */
  select max(CODIGO) FROM CARNETS INTO :ultimo;
  suspend;
end^

SET TERM ; ^

GRANT SELECT ON CARNETS TO PROCEDURE ULTIMO_CARNET;

GRANT EXECUTE ON PROCEDURE ULTIMO_CARNET TO SYSDBA;

Espero haberme explicado bien:D

Un saludo.

maeyanes 18-11-2008 20:50:38

Hola...

Vaya, eso si es muy raro. ¿Será que haces algo mal?

Yo hasta ahora no he tenido ningún tipo de problema haciendo procedimientos almacenados recibiendo y devolviendo parámetros.

De la forma en que te lo puse te debería funcionar sin problemas...


Saludos...

Al González 18-11-2008 21:11:13

Intuyo que Luis se ve obligado a usar Execute Statement porque el parámetro Anyo llega con un par de comillas laterales como parte del valor en sí. Lo cual significaría que desde Delphi lo forma él de esa extraña manera. :confused:

Al González 18-11-2008 21:16:48

Cita:

Empezado por Luis M. (Mensaje 326647)
...Desde Delphi lo llamo así:
Código Delphi [-]
//--- Introduce último recibo ---
  SP.StoredProcName := 'ULTIMO_RECIBO';
  SP.Prepare;
  try
    SP.Params.ParamValues['ANYO'] := QuotedStr(RecibosANYO.AsString);
    SP.ExecProc;
    cText := IntToStr(SP.ParamByName('ULTIMO').AsInteger + 1);
    RecibosNUMERO.AsString := Copy('00000', 1, 5 - Length(cText)) + cText;
  finally
    SP.UnPrepare;
  end;
...

¡Ah, pues sí! De hecho ahí se las está metiendo. :eek:

Luis M. 18-11-2008 21:29:16

Si le paso el parámetro sin el QuotedStr, no funciona, no me devuelve el valor requerido.
Cosa rara.:cool:


maeyanes 18-11-2008 21:33:41

Hola...

Por que así como lo estás haciendo estás concatenando cadenas para poder hacer el Execute Statement; si lo haces de la forma en que te puse, no deberías tener problemas...



Saludos...

Al González 18-11-2008 21:33:49

Cita:

Empezado por Luis M. (Mensaje 326695)
Si le paso el parámetro sin el QuotedStr, no funciona, no me devuelve el valor requerido.
Cosa rara.:cool:


:p Eso es porque usas Execute Statement. Prueba como te dice maeyanes y sin emplear QuotedStr. Verás que te funciona como esperas. :)

Lepe 18-11-2008 22:02:11

Pues ese tipo de fallo me parece los típicos que cometemos a altas horas de la noche o cuando estás muy agobiado. Modificas una cosa, no sale, ahora modificas otra.... hasta que todo "cuadra", eso sí, terminas sin saber cómo lo hiciste :D :D.

Saludos y me alegro que se aclarase el tema.

Luis M. 18-11-2008 22:16:35

Cita:

Empezado por Lepe (Mensaje 326704)
Pues ese tipo de fallo me parece los típicos que cometemos a altas horas de la noche o cuando estás muy agobiado. Modificas una cosa, no sale, ahora modificas otra.... hasta que todo "cuadra", eso sí, terminas sin saber cómo lo hiciste :D :D.

Saludos y me alegro que se aclarase el tema.

Me parece que tienes toda la razón:D:D:D


La franja horaria es GMT +2. Ahora son las 16:12:08.

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