Compartir a través de


Actualizar datos

Para actualizar datos, puede usar almacenamiento en búfer, transacciones o vistas.

Realizar actualizaciones con almacenamiento en búfer

Después de elegir el método de almacenamiento en búfer y el tipo de bloqueo, active el almacenamiento en búfer de registros o tablas.

Para activar el almacenamiento en búfer

Elija una de las opciones siguientes:

  • En el Diseñador de formularios, establezca la propiedad BufferModeOverride del cursor en el entorno de datos del formulario.

    –O bien–

  • En el código, establezca la propiedad Buffering.

    Por ejemplo, puede activar el almacenamiento pesimista de filas en búfer si coloca el código siguiente en el procedimiento Init de un formulario:

    CURSORSETPROP('Buffering', 2)
    

Después escriba código para las operaciones de actualización en el código del método apropiado para los controles.

Para escribir las modificaciones en la tabla original, utilice TABLEUPDATE( ). Para cancelar las modificaciones después de una operación errónea de actualización en una tabla restringida por reglas, utilice TABLEREVERT( ). TABLEREVERT( ) es válido aunque el almacenamiento explícito de tablas en búfer no esté activado.

El ejemplo siguiente demuestra cómo actualizar registros cuando está activado el almacenamiento pesimista de registros en búfer.

Ejemplo de actualización mediante búferes de registros y tablas

Código Comentarios
OPEN DATABASE testdata
USE customers
CURSORSETPROP('Buffering', 2)         
En el código Init del formulario, abre la tabla y activa el almacenamiento pesimista de registros en búfer.
lModified = .F.
FOR nFieldNum = 1 TO FCOUNT()         
   IF GETFLDSTATE(nFieldNum) = 2      
      lModified = .T.
      EXIT
   ENDIF
ENDFOR

Se desplaza por campos y comprueba los que se han modificado.

Nota   Este código podría estar en el evento Click de un botón de comando "Guardar" o "Actualizar".

IF lModified
   nResult = MESSAGEBOX;
      ("Record has been modified. Save?", ;
      4+32+256, "Data Change")
Busca el siguiente registro modificado.
   IF nResult = 7                  
      TABLEREVERT (.F.)            
   ENDIF
ENDIF
Muestra el valor actual y da al usuario la posibilidad de invertir el cambio del campo actual.
SKIP                           
IF EOF()
   MESSAGEBOX( "already at bottom")
   SKIP -1
ENDIF 
THISFORM.Refresh



SKIP garantiza que se escribe el último cambio.

Administrar actualizaciones mediante transacciones

Incluso si utiliza el almacenamiento en búfer pueden surgir problemas. Si desea proteger las operaciones de actualización y recuperar una sección completa de código como una unidad, utilice transacciones.

La incorporación de transacciones a su aplicación proporciona una protección adicional al almacenamiento en búfer de registros y tablas de Visual FoxPro, situando una sección completa de código en una unidad protegida y recuperable. Puede anidar transacciones y emplearlas para proteger actualizaciones almacenadas en búfer. Las transacciones de Visual FoxPro sólo están disponibles con tablas y vistas pertenecientes a una base de datos.

Ajustar segmentos de código

Una transacción actúa como un empaquetador que almacena en memoria caché o en disco las operaciones de actualización de datos, en lugar de aplicar directamente esas actualizaciones a la base de datos. La actualización real de la base de datos se realiza al final de la transacción. Si por alguna razón el sistema no puede realizar las operaciones de actualización en la base de datos, podrá deshacer la transacción completa para que no se lleve a cabo ninguna actualización.

Nota   Las operaciones de actualización almacenadas en búfer que se hayan realizado fuera de una transacción se ignoran en otra transacción de la misma sesión de datos.

Comandos que controlan transacciones

Visual FoxPro proporciona tres comandos y una función para administrar una transacción:

Para Utilice
Iniciar una transacción BEGIN TRANSACTION
Determinar el nivel actual de la transacción TXNLEVEL( )
Invertir todos los cambios realizados desde la instrucción BEGIN TRANSACTION más reciente ROLLBACK
Bloquear registros, grabar en disco todos los cambios realizados en tablas de la base de datos desde la instrucción BEGIN TRANSACTION más reciente y, a continuación, desbloquear los registros END TRANSACTION

Puede utilizar transacciones para ajustar modificaciones de tablas, archivos .cdx estructurales y archivos memo asociados a tablas de una base de datos. Las operaciones en las que intervienen variables y otros objetos no respetan las transacciones, por lo que no se pueden deshacer o confirmar.

Nota   Cuando se utilicen los datos almacenados en tablas remotas, el control de los comandos de la transacción sólo actualiza los datos de la copia local del cursor de la vista; las actualizaciones de tablas base remotas no se ven afectadas. Para habilitar las transacciones manuales en tablas remotas, utilice SQLSETPROP( ) y, a continuación, controle la transacción mediante SQLCOMMIT( ) y SQLROLLBACK( ).

En general, deberá utilizar las transacciones con búferes de registro en lugar de almacenamiento de tablas en búfer, salvo para ajustar llamadas de TABLEUPDATE( ). Si incluye un comando TABLEUPDATE( ) en una transacción, podrá deshacer una actualización errónea, resolver la causa del error y, a continuación, volver a intentar TABLEUPDATE( ) sin perder datos. De este modo se asegurará de que la actualización se produce como una operación de "todo o nada".

Aunque el procesamiento simple de transacciones proporciona operaciones seguras de actualización de datos en situaciones normales, no ofrece protección total contra errores del sistema. Si se interrumpe el suministro eléctrico o el sistema queda bloqueado durante el procesamiento del comando END TRANSACTION, se puede producir un error en la actualización de datos.

Utilice el código siguiente como plantilla para transacciones:

BEGIN TRANSACTION   
* Update records
IF lSuccess = .F. && an error occurs
   ROLLBACK
ELSE && commit the changes
   * Validate the data
   IF && error occurs
      ROLLBACK
   ELSE 
      END TRANSACTION
   ENDIF
ENDIF

Usar transacciones

Las reglas siguientes se aplican a las transacciones:

  • Una transacción comienza con el comando BEGIN TRANSACTION y termina con el comando END TRANSACTION o ROLLBACK. Una instrucción END TRANSACTION que no vaya precedida de una instrucción BEGIN TRANSACTION generará un error.
  • Una instrucción ROLLBACK que no vaya precedida de una instrucción BEGIN TRANSACTION generará un error.
  • Una vez comenzada una transacción, permanecerá en vigor hasta que comience el END TRANSACTION correspondiente (o hasta que se ejecute un comando ROLLBACK), incluso en programas y funciones, a menos que se termine la aplicación, lo que producirá una anulación.
  • Visual FoxPro utiliza datos almacenados en caché en el búfer de transacciones antes de utilizar datos del disco para consultas acerca de los datos relacionados con transacciones. De este modo se asegura la utilización de los datos más actuales.
  • Si la aplicación termina durante una transacción, todas las operaciones se desharán.
  • Una transacción sólo funciona en un contenedor de base de datos.
  • No es posible utilizar el comando INDEX si sobrescribe un archivo de índice existente o si hay abierto algún archivo de índice .cdx.
  • Las transacciones pertenecen al ámbito de las sesiones de datos.

Las transacciones presentan los comportamientos de bloqueo siguientes:

  • En una transacción, Visual FoxPro impone un bloqueo en el momento en que un comando lo solicita directa o indirectamente. Cualquier comando de desbloqueo directo o indirecto procedente del sistema o del usuario se almacena en caché hasta que se complete la transacción mediante los comandos ROLLBACK o END TRANSACTION.
  • Si utiliza un comando de bloqueo como FLOCK( ) o RLOCK( ) en una transacción, la instrucción END TRANSACTION no liberará el bloqueo. En ese caso, deberá desbloquear explícitamente todos los bloqueos realizados en una transacción. Las transacciones que contienen los comandos FLOCK( ) o RLOCK( ) deben ser lo más breves posible; en caso contrario, los usuarios podrían encontrar los registros bloqueados durante mucho tiempo.

Anidar transacciones

Las transacciones anidadas proporcionan grupos lógicos de operaciones de actualización de tablas que están aislados de los procesos concurrentes. No es necesario que los pares BEGIN TRANSACTION...END TRANSACTION estén en la misma función o el mismo procedimiento. Las transacciones anidadas tienen estas reglas:

  • Se pueden anidar hasta cinco pares BEGIN TRANSACTION...END TRANSACTION.
  • Las actualizaciones realizadas en una transacción anidada no se confirman hasta que se llama a la instrucción END TRANSACTION más externa.
  • En las transacciones anidadas, una instrucción END TRANSACTION sólo funciona sobre la transacción iniciada por la última instrucción BEGIN TRANSACTION ejecutada.
  • En las transacciones anidadas, una instrucción ROLLBACK sólo funciona sobre la transacción iniciada por la última instrucción BEGIN TRANSACTION ejecutada.
  • La actualización más interna de una serie de transacciones anidadas sobre los mismos datos tiene prioridad sobre todas las demás del mismo bloque de transacciones anidadas.

Observe en el ejemplo siguiente que, puesto que los cambios en una transacción anidada no se escriben en disco, sino en el búfer de transacciones, la transacción más interna sobrescribirá los cambios realizados en los mismos campos STATUS de la transacción anterior:

BEGIN TRANSACTION &&  transaction 1
   UPDATE EMPLOYEE ; &&  first change
      SET STATUS = "Contract" ;
      WHERE EMPID BETWEEN 9001 AND 10000
   BEGIN TRANSACTION &&  transaction 2
      UPDATE EMPLOYEE ;
         SET STATUS = "Exempt" ;
         WHERE HIREDATE > {^1998-01-01}  &&  overwrites
   END TRANSACTION &&  transaction 2
END TRANSACTION    &&  transaction 1

El siguiente ejemplo de transacción anidada elimina un registro de cliente y todas las facturas relacionadas. La transacción se deshará si se producen errores durante un comando DELETE. Este ejemplo demuestra la agrupación de operaciones de actualización de tablas para impedir que las actualizaciones se completen parcialmente y para evitar conflictos de simultaneidad.

Ejemplo de modificación de registros en transacciones anidadas

Código Comentarios
DO WHILE TXNLEVEL( ) > 0
   ROLLBACK
ENDDO
Limpia el entorno de otras transacciones.
CLOSE ALL
SET MULTILOCKS ON
SET EXCLUSIVE OFF
Establece el entorno para el almacenamiento en búfer.
OPEN DATABASE test
USE mrgtest1
CURSORSETPROP('buffering',5)
GO TOP


Activa el almacenamiento optimista de tablas en búfer.
REPLACE fld1 WITH "changed"
SKIP
REPLACE fld1 WITH "another change"
MESSAGEBOX("modify first field of both" + ;
   "records on another machine")
Cambia un registro.

Cambia otro registro.
BEGIN TRANSACTION
lSuccess = TABLEUPDATE(.T.,.F.)
Inicia la transacción 1 e intenta actualizar todos los registros modificados que no están vigentes.
IF lSuccess = .F.
   ROLLBACK
   AERROR(aErrors)
   DO CASE 
   CASE aErrors[1,1] = 1539            
   ...
   CASE aErrors[1,1] = 1581            
   ...
   CASE aErrors[1,1] = 1582            
Si se producen errores en la actualización, deshace la transacción.
Obtiene un error de AERROR( ).
Determina la causa del error.
Si un desencadenador ha producido errores, soluciona el problema.

Si un campo no acepta valores nulos, soluciona el problema.
Si se ha infringido una regla de campo, soluciona el problema.
   CASE aErrors[1,1] = 1585            
      nNextModified = getnextmodified(0)
      DO WHILE nNextModified <> 0
         GO nNextModified
         RLOCK()
         FOR nField = 1 to FCOUNT()
            cField = FIELD(nField)
            if OLDVAL(cField) <> CURVAL(cField)
Si otro usuario ha modificado un registro, localiza el primer registro modificado.
Hace un bucle a través de todos los registros modificados, comenzando con el primero.
Bloquea cada registro para asegurarse de que se puede actualizar.
Comprueba si hay cambios en cada campo.

Compara el valor almacenado en búfer con el valor en disco; a continuación, muestra un cuadro de diálogo al usuario.
               nResult = MESSAGEBOX;
               ("Data was changed " + ;
                "by another user — keep"+ ;
                "changes?", 4+48, ;
                "Modified Record")
 
               IF nResult = 7
                  TABLEREVERT(.F.)
                  UNLOCK record nNextModified
               ENDIF
Si el usuario responde "No", invierte el registro uno y lo desbloquea.
               EXIT
            ENDIF
         ENDFOR
Sale del bucle "FOR nField...".
      ENDDO
Obtiene el siguiente registro modificado.
      BEGIN TRANSACTION
      TABLEUPDATE(.T.,.T.)
      END TRANSACTION
      UNLOCK
Inicia la transacción 2 y actualiza todos los registros no invertidos que están en vigor.
Finaliza la transacción 2.
Libera el bloqueo.
   CASE aErrors[1,1] = 109   
   ...
   CASE aErrors[1,1] = 1583    
   ...
   CASE aErrors[1,1] = 1884    
   ...
   OTHERWISE
      MESSAGEBOX( "Unknown error "+;
      "message: " + STR(aErrors[1,1]))
   ENDCASE
Si el registro está en uso por otro usuario, soluciona el problema.

Si se ha infringido una regla de fila, soluciona el problema.

Si hubo una infracción de índice único, soluciona el problema.

De lo contrario, muestra un cuadro de diálogo al usuario.
ELSE
   END TRANSACTION
ENDIF

Termina la transacción 1.

Proteger actualizaciones remotas

Las transacciones pueden protegerle frente a los errores generados por el sistema durante actualizaciones de datos en tablas remotas. El ejemplo siguiente utiliza una transacción para ajustar operaciones de escritura de datos en una tabla remota.

Ejemplo de una transacción de una tabla remota

Código Comentarios
hConnect = CURSORGETPROP('connecthandle')
SQLSETPROP(hConnect, 'transmode',
DB_TRANSMANUAL)
Obtiene el controlador de conexión
y activa transacciones manuales.
BEGIN TRANSACTION
Comienza la transacción manual.
lSuccess = TABLEUPDATE(.T.,.F.)
IF lSuccess = .F.
   SQLROLLBACK (hConnect)
   ROLLBACK
Intenta actualizar todos los registros que no están en vigor.
Si la actualización ha producido errores,
deshace la transacción en
la conexión del cursor.
   AERROR(aErrors)
   DO CASE 
Obtiene un error de AERROR( ).
   CASE aErrors[1,1] = 1539            
   ...
Si un desencadenador ha producido errores, soluciona el problema.
   CASE aErrors[1,1] = 1581            
   ...
Si un campo no acepta valores nulos, soluciona el problema.
   CASE aErrors[1,1] = 1582            
   ...
Si se ha infringido una regla de campo, soluciona el problema.
   CASE aErrors[1,1] = 1585            
      nNextModified = GETNEXTMODIFIED(0)
      DO WHILE nNextModified <> 0
         GO nNextModified
Si otro usuario ha cambiado un registro, soluciona el problema.

Hace un bucle a través de todos los registros modificados, comenzando con el primero.
         FOR nField = 1 to FCOUNT()
            cField = FIELD(nField)
            IF OLDVAL(cField) <> CURVAL(cField)
               nResult = MESSAGEBOX;
               ("Data has been changed ;
               by another user. ;
               Keep changes?",4+48,;
               "Modified Record")
Comprueba si hay cambios en cada campo.

Compara el valor almacenado en búfer
con el valor del disco y, a continuación, muestra un cuadro de diálogo al usuario.
               IF nResult = 7
                  TABLEREVERT(.F.)
               ENDIF
               EXIT
            ENDIF
         ENDFOR
         nNextModified = ;
         GETNEXTMODIFIED(nNextModified)
      ENDDO
Si el usuario respondió "No",
invierte el registro uno.

Sale del bucle "FOR nField...".


Obtiene el siguiente registro modificado.
      TABLEUPDATE(.T.,.T.)
      SQLCOMMIT(hConnect)
Actualiza todos los registros no invertidos que están vigentes y ejecuta una confirmación.
   CASE aErrors[1,1] = 109
         * Handle the error
El error 109 indica que otro usuario está utilizando el registro.
   CASE aErrors[1,1] = 1583
         * Handle the error
El error 1583 indica que se ha infringido una regla de la fila.
   CASE aErrors[1,1] = 1884
         * Handle the error
El error 1884 indica que se ha infringido la unicidad del índice.
   OTHERWISE
         * Handle generic errors.
 
      MESSAGEBOX("Unknown error message:" ;
        + STR(aErrors[1,1]))
   ENDCASE
Muestra un cuadro de diálogo al usuario.

Fin del control de errores.
ELSE
   SQLCOMMIT(hConnect)
   END TRANSACTION
ENDIF

Si se controlaron todos los errores y la transacción completa se ha realizado con éxito, ejecuta una confirmación y termina la transacción.

Vea también

Almacenar datos en búfer | Administrar el rendimiento | Programar para acceso compartido | TABLEUPDATE( ) | TABLEREVERT( ) | Controlar el acceso a datos | Administrar conflictos