PDA

Ver la Versión Completa : Comprobar en evento OnExit si valor de campo ya existe


JMGR
07-05-2008, 23:15:16
Buenas!!!

Despues de mucho tiempo desconectado de las bases de datos ahora tengo que volver a "entretenerme" con ellas y estoy calentando con un programa sencillito de prueba pero mis conocimientos estan un poco oxidados...Seguramente esto es algo que habran preguntado miles de veces pero he estado buscando por el foro y googleando y no lo he encontrado...o no he sabido buscar...:o

Trabajo con Delphi5 y Access a traves de ADO, tengo una tabla con dos campos: Codigo y Cliente. Los registros se añaden a traves de un form con dos DBEdit y la idea es que cuando se añada un nuevo registro, al terminar de escribir el Codigo, es decir, en el evento OnExit del correspondiente DBEdit se compruebe si ese codigo de cliente ya existe en la base de datos para evitar duplicados. Para hacer esto creo recordar que usaba algo parecido al siguiente codigo (despues de, evidentemente, llamar al metodo Insert):

procedure TClientes.DBEdit1Exit(Sender: TObject);
begin
if DataModule3.TablaClientes.Locate('Codigo',dbedit1.text,[]) then
begin
if MessageDlg('Este codigo ya existe, ¿ desea modificar el cliente al que corresponde?', mtWarning, [mbYes,mbNo], 0) = mryes then
DataModule3.TablaClientes.edit;
Dbedit1.SetFocus;
end;
end;


pero me da un error que dice algo asi como:

"Los cambios solicitados en la tabla no se realizaron correctamente porque crearian valores duplicados en el indice, clave principal o relacion...y bla, bla, bla"

He estado mirando el codigo de antiguos programas y basicamente hago lo mismo solo que en aquellos funciona...

Es muy probable que este cometiendo un error evidente pero como ya dije llevo bastante tiempo desconectado de las bases de datos y no lo encuentro...

Mi duda es si el metodo LOCATE implica un POST cuando es llamado porque si no, no entiendo que "cambios" son los "solicitados" aludidos en el error...:confused:

Un poco de luz por favor...

Gracias y un saludo!
JMGR

BlueSteel
07-05-2008, 23:31:51
Hola...

y para ingresar los datos utilizas DBEdit o Edit ???

yo utilizo Edit.. y en el evento onExit pongo lo sgte (Estoy utilizando SQL Server - Delphi 7 y ADO para el SQL )

AQ_Consulta.Close;
AQ_Consulta.SQL.Clear;
AQ_Consulta.SQL.Add('Select 1 From CCosto');
AQ_Consulta.SQL.Add('Where Ccto_Cod =:Var1');
AQ_Consulta.Parameters[0].Value := vCod.Text;
AQ_Consulta.Open;
If AQ_Consulta.Eof Then vDet.SetFocus
Else
Begin
ShowMessage('Ya fue registrado un Centro de Costo con ese Codigo...');
vCod.Clear;
vCod.SetFocus;
End;


AQ_Consulta es un ADOQuery en donde pregunta si existe el codigo.. si es EOF (Fin de Archivo)...no existe y me pasa el sgte campo (vDet.Setfocus), de lo contrario existe y envio mensajes ... limpio variables


Salu2:p:D

coso
08-05-2008, 00:02:56
Si. El metodo locate implica un post.

El problema q tienes es q el DBEdit es q ya actualiza la base de datos (hace un 'post') al salir, por lo que si el 'codigo' ya existe, te genera el error de 'valores duplicados' antes de llegar a tu codigo para encontrar el error. En otras palabras, el error q estas intentando 'pillar' lo detecta el servidor de base de datos antes. Deberias colocar el mensaje de error en el evento onPostError de la tabla, olvidandote de comprobar si ya existe ese codigo o no. Si lo que quieres es algo mas concreto, deberias buscar para 'cazar' excepciones.

Un par de cosas que he visto q tambien son extrañas:

- Si haces un locate, estas reposicionando el cursor en la base de datos por lo que si tienes la opcion UpdateWhereAll activada (que me parece q es por defecto), en el momento de salir estas cambiando el valor del dbedit :( antes que se actualizen los cambios.

- Veo un Table.Edit pero no veo ningun post. Parece q dejas abierta la tabla en modo edicion. Lo correcto (para q no te pasen errores como este, pues si la dejas en Table.State = dsEdit te puede pasar de todo) es hacer

Table.Edit;
Table.FieldValues['NOMBRE'] := 'nombre de ejemplo';
Table.FieldValues['TEL'] := '9355555555';
...
Table.Post;

Al entrar a los dbedits, estos automaticamente abren la tabla en modo edicion, y al salir postean, por lo que es innecesario incluir ese edit aqui.

La mejor manera de hacer estas cosas es no usar los dbedits, y si usar el evento onexit de un edit normal y corriente, editando como te ponia antes. Si te empeñas con los dbedit, no uses el locate o filtros o nada parecido dentro de ellos (ni el next, prior, ni indexfieldnames...) pues si mueves el cursor de la base de datos ya la pifiaste :p

PD: borland dejo de desarrollar sobre las TTable porque tenian (y provocaban) infinidad de bugs, te recomiendo q uses SQL, es mucho mas sencillo trabajar con ello ;)


Venga, espero haberte servido de ayuda :D

Lepe
08-05-2008, 08:41:00
En lugar de Locate puedes usar LookUp para ver si el código ya existe sin cambiar el cursor del registro.

Saludos

Delfino
09-05-2008, 11:08:05
Si. El metodo locate implica un post.
Quien ha dicho esto?
En lugar de Locate puedes usar LookUp para ver si el código ya existe sin cambiar el cursor del registro.
O mejor dejar q el manejador de BD lo compruebe (lo haria mucho mas rapido) y tratar el error en el evento OnPostError del Dataset..

coso
09-05-2008, 12:50:51
Cuando llamas a edit, la TTable.State esta en dsEdit. Cuando llamas a locate, la TTable.State pasa a dsBrowse. Si tienes activado UpdateWhereAll en la tabla, que es por defecto, se hace un post automatico. Compruebalo con algun programilla o el debbuger...

Saludos

JMGR
16-05-2008, 20:46:44
Bueno primero que nada pido perdon por la tardanza en dar noticias pero es que tuve que irme de viaje y no pude conectarme hasta ahora...gracias por la multitud de respuestas.

En principio de las soluciones propuestas la que mejor entiendo es la de BlueSteel de hacerlo con un Query pero no se si es "matar mosquitos a cañonazos"...

El metodo LookUp ya lo he probado pero no me vale, por lo menos poniendolo en el OnExit, ya que sigue saltando el error...a lo mejor poniendolo en otro sitio pero no tengo claro donde...:confused:

En cuanto a controlar el evento OnPostError estoy en ello pero me esta costando bastante entender como debo usarlo...:(

Para empezar he probado a quitar el codigo del evento OnExit del DBEdit y no me salta ningun error aunque haya introducido un valor ya existente de lo cual deduzco que al salir del DBEdit no se produce automaticamente un POST...¿o si..?

Este codigo me produce un bucle infinito, se el por qué, pero no me aclaro de como hacerlo:

procedure TDataModule3.TablaClientesPostError(DataSet: TDataSet;
E: EDatabaseError; var Action: TDataAction);
begin
if (E is EDatabaseError) then
if MessageDlg('Parece que ese Codigo ya existe, ¿ Desea modificar el registro '+#13+#10+'con ese Codigo?', mtWarning, [mbYes,mbNo], 0) = mrYes then
begin
Action:=daAbort;
TablaClientes.Locate('Codigo',Clientes.dbedit1.text,[]); //<--Esto debe ser lo que provoca el bucle
TablaClientes.edit;
Clientes.Dbedit1.SetFocus;
end
else
begin
ACtion:=daAbort;
Tablaclientes.insert;
Clientes.Dbedit1.SetFocus;
end;
end;


¿Donde estoy tropezando?

PD:¿ Como puedo cambiar la opcion UpdateWhereAll?

Edito:
A la espera de respuestas por ahora lo he solucionado comprobando si existe el valor del campo con otro componente ADOTable apuntando a la misma tabla...

coso
16-05-2008, 22:18:31
no no al salir del DBEdit no se produce un post mil excusas. Eso si, el locate si que hace updates a la base de datos (no me preguntes cuando, creo q con el UpdateWhereAll activado pero no estoy seguro. Personalmente, estoy escarmentado de mover el cursor de una tabla si esta en modo edit). Si tu mueves el cursor de la tabla (locate, gotonearest, etc...) el valor de DBEdit se actualiza solo. Prueba de hacer, en el momento en que hagas post, olvidandote de los eventos, algo asi:



try
TablaClientes.Post;
except
// Codigo si hay error.
raise exception.Create('error actualizando tabla. Posibles indices duplicados'); // se sale de la funcion
end;

// resto de codigo si no hay error en post.


Venga, a ver si te sirve.

PD: La propiedad UpdateMode de la tabla o query puede tener la opcion UpdateWhereAll, UpdateWhereChanged y UpdateKeyOnly
PDD : Si el codigo cliente es un indice, no seria mejor q lo adjudicase de manera automatica? En el caso q no, en el evento OnExit del dbedit del codigo cliente (poco elegante pero efectivo)



try
Tabla.Post;
Tabla.Edit;
except
ShowMessage('Este codigo ya existe');
DBEdit_codigo_cliente.SelectAll;
DBEdit_codigo_cliente.SetFocus;
end;

Lepe
16-05-2008, 22:33:28
Tus intuiciones son ciertas JMGR

Cuando se produce el OnPostError, debes tener en cuenta que tienes un registro en Edición (dsEdit o dsInsert) que está modificado pero aún no se ha guardado ese cambio, es más, no se puede guardar porque ha producido un error.

Pero que no se te escape otro detalle, la Tabla se encuentra apuntando a ese mismo registro que da error.

Tienes las alternativas del parámetro Action (no recuerdo cuales son :o), pero tal y como veo tu código, no veo alternativas para tu usuario, es decir, tu usuario no podrá continuar hasta que cambie el código de ese cliente y al guardar no dé errores:


procedure TDataModule3.TablaClientesPostError(DataSet: TDataSet;
E: EDatabaseError; var Action: TDataAction);
begin
//if (E is EDatabaseError) then // siempre lo será, porque es de ese tipo el parámetro.
MessageDlg('Parece que ese Codigo ya existe.' +#13+#10+'Introduzca otro por favor', mtWarning, [mbYes], 0) ;
Action:=daAbort;
end;

Con eso, informas al usuario y dejas el registro en edición, esperando a que cambie el código.

A otra cosa modosa: ¿estás seguro que el OnPostError se produce por el fallo del código de forma exclusiva? Obviamente no, ocurrirá cuando:

- Si un campo es Requerido (not null) pero tiene el valor null
- Un índice único sobre algún campo está a punto de violarse (
- Por pérdida repentina de conexión a la base de datos (... no sé si está en red o no).
- etc.

Para saber el error que ha ocurrido, tendrás que indagar en las "ADOExceptions" para obtener el código de error... y aquí será mejor que un entendido en ADO hable mejor que yo. Básicamente sería algo así como:

dentro del OnPostError
if E is EADODBexception then // me lo he inventado
if E.ErrorCode = const_KeyViolation then
ShowMessage('Codigo Repetido de cliente')
else if E.ErrorCode = const_Indice_Unico_A_punto_de_Violar then
ShowMessage('Por favor cambia el campo NombreCliente');

JMGR
18-05-2008, 03:42:31
Bueno de momento lo he solucionado con la poco elegante pero efectivisima forma propuesta por coso ( no puedo generarlos de manera automatica porque esos codigos ya estan creados anteriormente):

Tabla.Post;
Tabla.Edit;


en el evento OnExit del DBEdit, y controlando el error en el OnPostError

Lepe:

Con eso, informas al usuario y dejas el registro en edición, esperando a que cambie el código.


Mi idea es que si se introduce un codigo ya existente exista la opcion de modificarlo o de introducir otro, y me interesa que se compruebe nada mas meter el codigo...
La mejor forma, como indicas, seria identificar el error segun su codigo y comprobar que es debido a un valor duplicado pero he estado buscando y no he encontrado ninguna lista de los errores asi que por ahora me conformo con que funcione...:rolleyes:

Cualquier sugerencia mas sera bienvenida...:D
Gracias por el interes y sobre todo por las respuestas.
Un saludo.
JMGR