Mostrando entradas con la etiqueta tablas. Mostrar todas las entradas
Mostrando entradas con la etiqueta tablas. Mostrar todas las entradas

domingo, febrero 25, 2018

VFP: Cómo mover una tabla de una Base de Datos a otra

En ocasiones puede pasar que tengamos una tabla que inicialmente pertenece a una base de datos, pero que luego por algún motivo necesitemos quitarla de esa base de datos y moverla a otra distinta con todos sus datos.

A priori esto puede parecer algo sencillo, pero dependiendo de algunas circunstancias puede ser bastante más complicado.

Veamos algunos ejemplos, para los que VFP debe estar ubicado en el directorio de los datos.
Recordar siempre hacer un backup antes!




Caso 1: Tabla con nombres de campo cortos (hasta 10 caracteres) y sin triggers


Supongamos por ejemplo que tenemos una "tabla1" con un campo "NOMBRE" y un índice por el mismo, perteneciente a la base de datos BDD1

En este caso la operación es sencilla, basta con quitar la tabla de una base de datos y agregarla a la otra base de datos:


1) Para definir una situación inicial de ejemplo, creamos una BDD1 con una Tabla1 con datos e índice (en un caso real ir directo al paso 2):

CREATE DATABASE bdd1
CREATE TABLE tabla1 (nombre c(30))
INSERT INTO tabla1 (nombre) VALUES ("Francisco")
INSERT INTO tabla1 (nombre) VALUES ("Antonio")
INSERT INTO tabla1 (nombre) VALUES ("Juan")
INDEX on nombre TAG nombre
CLOSE ALL


2) Ahora movemos la tabla a la otra BDD:

OPEN DATABASE bdd1 EXCLUSIVE && Abrimos la BDD origen 
REMOVE TABLE tabla1          && Quitamos la tabla de la misma 
OPEN DATABASE bdd2 EXCLUSIVE && Abrimos la BDD destino
ADD TABLE tabla1             && Agregamos la tabla
CLOSE ALL


Listo! Esta opción es la más simple y rápida, y no implica trasvasar datos.




Caso 2: Tabla con nombres de campo largos (más de 10 caracteres) y sin triggers


Ahora supongamos por ejemplo que tenemos una "tabla1" con un campo "NOMBRE_LARGO" y un índice por el mismo, perteneciente a la base de datos BDD1

En este caso la operación sigue siendo sencilla, aunque no podremos seguir los pasos del Caso 1 porque perderemos los nombres largos y los índices que dependan de ellos dejarán de funcionar.


1) Para definir una situación inicial de ejemplo, creamos una BDD1 con una Tabla1 con datos e índice (en un caso real ir directo al paso 2):

CREATE DATABASE bdd1CREATE TABLE tabla1 (nombre_largo c(30))
INSERT INTO tabla1 (nombre_largo) VALUES ("Francisco")
INSERT INTO tabla1 (nombre_largo) VALUES ("Antonio")
INSERT INTO tabla1 (nombre_largo) VALUES ("Juan")
INDEX on nombre_largo TAG nombre
CLOSE ALL


2) Ahora movemos la tabla a la otra BDD en un directorio de trabajo y traemos todo lo hecho:

MKDIR copia                     && Creamos un dir de trabajo
COPY FILE BDD2.* TO copia\*.*   && Copiamos la BDD destino
USE tabla1 EXCLUSIVE

COPY STRUCTURE TO copia\tabla1 ;&& Copiamos la estructura de la
   WITH CDX DATABASE copia\BDD2 && tabla y CDX a la BDD destino
CLOSE ALL
OPEN DATABASE copia\bdd2 EXCLU  && Abrimos la BDD destino
USE copia\tabla1 EXCLUSIVE

APPEND FROM tabla1              && Recuperamos los datos originales
CLOSE ALL
OPEN DATABASE bdd1 EXCLUSIVE    && Abrimos la BDD origen
REMOVE TABLE tabla1             && Quitamos la tabla de la misma
CLOSE ALL 
COPY FILE copia\*.* TO *.*      && Traemos los archivos modificados


Terminado! Esta opción es la más simple y rápida, aunque implica trasvasar datos.




Caso 3: Tabla con propiedades de BDD


Otro caso que se puede presentar es tener una tabla asociada a una BDD que use propiedades específicas de esta, como reglas de validación de campos, mensajes de validación, comentarios de tabla o campos, triggers, etc.

Este caso es uno de los más complejos, porque si seguimos los pasos del Caso 1, perderemos los nombres largos y los índices que dependan de ellos dejarán de funcionar., y si seguimos los pasos del  Caso 2 tendremos los datos y los índices, pero no las propiedades de BDD comentadas.

Aquí se podría usar FoxBin2Prg, pero en este caso es necesario conocer las estructuras DB2 (tabla) y DC2 (base de datos) generadas, ya que habrá que cortar y pegar algunas secciones del DBC1 para pasarlos al DBC2, además de modificar el DB2.

Vamos a ver un ejemplo con la misma tabla que el Caso 2 y algunas propiedades de BDD.


1) Para definir una situación inicial de ejemplo, creamos una BDD1 con una Tabla1 con datos, índice y algunas propiedades de BDD (en un caso real ir directo al paso 2):

CREATE DATABASE bdd1
CREATE TABLE tabla1 ;
    ( nombre_largo C(30) ;
    CHECK NOT EMPTY(nombre_largo) ;
    ERROR "El nombre no puede estar vacío" ;
    , codigo I AUTOINC NEXTVALUE 1 STEP 1 ;
    PRIMARY KEY )

INSERT INTO tabla1 (nombre_largo) VALUES ("Francisco")
INSERT INTO tabla1 (nombre_largo) VALUES ("Antonio")
INSERT INTO tabla1 (nombre_largo) VALUES ("Juan")
INDEX on nombre_largo TAG nombre
DBSETPROP("tabla1.nombre_largo","FIELD","Comment";
    ,"Comentario del campo nombre_largo")
DBSETPROP("tabla1","TABLE","Comment","Comentario de la tabla")
CLOSE ALL



2) Creamos un archivo de configuración específico para nuestra Tabla1 (tabla1.dbf.cfg) con esta configuración:

DBF_Conversion_Support: 8 && Permite exportar e importar estructura, indices y datos


3) Creamos la vista de texto (DB2) de esta Tabla, la vista de texto (DC2) de su base de datos BDD1 y la vista de texto (DC2) de la base de datos destino BDD2 con FoxBin2Prg


4) Si abrimos la vista de texto de la tabla del ejemplo (tabla1.db2), veremos que tiene dentro toda la información de la estructura al principio y luego los registros de datos. En esta cabecera debemos cambiar el nombre de la base de datos actual (BDD1) por la que queremos que sea la nueva (BDD2) y lo volvemos a guardar:

<TABLE>
    <MemoFile></MemoFile>
    <CodePage>1252</CodePage>
    <LastUpdate></LastUpdate>
    <Database>bdd2.dbc</Database>



5) Si abrimos la vista de texto de la base de datos del ejemplo (bdd1.dc2), veremos que figuran todas las tablas con una estructura como esta:

<DATABASE>
    <Name>BDD1</Name>
    <Comment></Comment>
    <Version>10</Version>
    <DBCEvents>.F.</DBCEvents>
    <DBCEventFilename></DBCEventFilename>

    <TABLES>

        <TABLE>
            <Name>TABLA1</Name>
            <Comment>Comentario de la tabla</Comment>
            <Path>tabla1.dbf</Path>

            ... 
        </TABLE>


Los tags <TABLE></TABLE> contienen toda la información de cada tabla, sus campos, índices y comentarios, parte de los cuales están repetidos en la información de la tabla.


6) La sección completa <TABLE></TABLE> de la tabla que nos interesa pasar, la cortamos de esta estructura y la pegamos en la estructura de destino (bdd2.dc2), cuidando de insertarla en orden alfabético por el nombre de la tabla (el tag <NAME>), y guardando el cambio en ambos archivos (bdd1.dc2 y bdd2.dc2)

Nota: Si la base de datos bdd2 es nueva, no tendrá tablas dentro por lo que la estructura externa <TABLES></TABLES> que contiene la tablas tampoco existirá, por lo que podría haber que crearla manualmente, ya que estamos pasando solo una tabla.


7) Abrimos el archivo de la base de datos origen (bdd1.dc2) con FoxBin2Prg para que regenere el DBC sin la Tabla1


8) Abrimos el archivo de la tabla (tabla1.db2) con FoxBin2Prg para que la regenere con su nueva base de datos referenciada


9) Abrimos el archivo de la base de datos destino (bdd2.dc2) con FoxBin2Prg para que regenere el DBC con la nueva información de la Tabla1


¡Al fin! Ya tenemos pasada la tabla1 de la bdd1 a la bdd2 con toda su información relacionada.




Resumen:


Hay otras formas de pasar una tabla a otra base de datos, pero normalmente requieren usar programación específica para dichos casos y tener un conocimiento más técnico y profundo de cada componente.

Lo que he querido mostrar en este caso es una forma genérica que sirve con cualquier tabla y cualquier base de datos, donde todo se reduce a cortar/pegar algunas secciones de un archivo de texto a otro y dejar que FoxBin2Prg se encargue de la parte técnica.

Nota importante sobre triggers: Se debe tener en cuenta que cuando hay triggers de por medio, si bien FoxBin2Prg puede pasar la información de una BDD a otra, este caso sí requiere tener conocimientos de los mismos y de procedimientos almacenados, ya que aunque se pase automáticamente la información de la tabla y sus metadatos, no será igual con los procedimientos almacenados, los cuáles habrá que seleccionar cuidadosamente y pasar también a la base de datos de destino, pero además de esto se debe considerar si es posible hacer esta mudanza de base de datos, porque normalmente los triggers vinculan tablas (ej; si se borra o actualiza en una que se borre o actualice en cascada en otras) y es muy probable que eso implique o bien quitar triggers o bien mover varias tablas en conjunto.


Hasta la próxima! :D


sábado, enero 11, 2014

¿Cómo usar una tabla FoxPro varias veces sin que se cierre?

[2015-04-24 Actualizado con nuevo ejemplo]

Por: Fernando D. Bozzo

El comando USE es muy potente y tiene varios parámetros que están muy bien explicados en la ayuda.

Normalmente en un sistema multiusuario las tablas se abren compartidas (SHARED) para que varios usuarios puedan usar la misma tabla a la vez, y además muchas veces se dá la necesidad de abrir una misma tabla más de una vez en diferentes lugares (forms, clases, programas, etc), y la cláusula AGAIN y ALIAS existen con ese fin:


USE <Tabla> SHARED AGAIN ALIAS Tabla_1


Muchos programadores no suelen anticipar el uso multiusuario de una tabla y simplemente la abren SHARED creyendo que eso es todo... pero luego ven que en un momento dado uno de los sitios que usan la tabla la cierra, y el otro que usaba lo mismo falla porque no la encuentra abierta, ¿qué hacer entonces?

Ayuda mucho tener una nomenclatura para las variables y los ALIAS, de manera que esto se puede resolver muy fácil.



Dos o más formularios usando la misma tabla


Esta es una situación muy habitual, tener varios formularios que usan la misma tabla simultáneamente y hasta puede que con distintos índices en cada sitio, pero compartiendo los mismos datos.

Una forma de poder aprovechar esos datos e independizarse de qué haga el resto del sistema con ella es esta:


* Form Clientes usando la tabla Precios
USE PRECIOS SHARED AGAIN ALIAS cli_PRECIOS

* Form Proveedores usando la tabla Precios
USE PRECIOS SHARED AGAIN ALIAS prv_PRECIOS


¿Y si la tabla se abre en el entorno de datos (DataEnvironment)?
Fácil, se abre el entorno de datos, se elige la tabla PRECIOS y en la ventana de Propiedades se le cambia el ALIAS como se indicó antes.

Otra opción es usar sesiones de datos privadas, de esa forma se puede usar el mismo nombre de tabla sin cambiarle el alias.



Rutinas de consulta (SQL, LOCATE, SEEK, etc)


Cuando se separa un sistema en N capas (por ejemplo: DATOS, INTERFAZ, NEGOCIO), se hacen los métodos de consulta por separado y generalmente en clases específicas para accesos a datos. Es en estos métodos donde suele haber también el mismo problema que con los forms, tablas que se abren en distintos métodos para hacer distintas consultas, y que si están bien encapsulados (aislados) deberían abrir la tabla, hacer la consulta, pasar los datos a una variable o array, cerrar la tabla y devolver los datos.

Esta sería una forma de método de consulta encapsulado y optimizado:

PROCEDURE consultarPrecioPorArticulo
   LPARAMETERS tcCodArticulo, taPrecio

   TRY
      LOCAL lnSelect, loEx as Exception
      lnSelect = SELECT()
      SELECT 0

      USE PRECIOS SHARED AGAIN NOUPDATE ALIAS __Precios

      SELECT Precio FROM __Precios ;
         WHERE CodArtic == tcCodArticulo ;
         INTO ARRAY taPrecio

   CATCH TO loEx
      THROW

   FINALLY
      USE IN (SELECT("__Precios")) && Siempre cierro la tabla
      SELECT (lnSelect) && Restauro el área de trabajo original
   ENDTRY
ENDPROC


En este caso, y por el carácter temporal de la apertura (abre, consulta, cierra) antecedo el ALIAS con uno o dos guiones bajos, que es lo que suelo usar en estos casos. También notar el NOUPDATE en este caso, que hace una apertura ReadOnly (de solo lectura), que es más eficiente y rápido que el de escritura cuando se sabe que no se quieren actualizar datos.



Encapsulación en métodos de Negocio


Un sitio donde es muy útil usar este mecanismo de USE AGAIN es en los métodos de negocio para favorecer la encapsulación.

Seguramente muchos conozcan esta forma de abrir tablas o reutilizar las ya abiertas:

LOCAL llAbiertaAqui
IF NOT USED("MITABLA")
   USE MITABLA IN 0
   llAbiertaAqui = .T.
ENDIF
SELECT MITABLA
SET RODER TO INDICE

*...(el código del método)

IF llAbiertaAqui
   USE
ENDIF



Esta simple rutina adolece de varios problemas, veamos:
  1. Abre la tabla sin indicar el tipo de acceso deseado (EXCLUSIVE, SHARED, NOUPDATE, etc)
  2. ¿Qué ocurre si al intentar reutilizar la tabla que había abierta, la misma estaba como Sólo-Lectura y resulta que vamos a necesitar usar REPLACE? => Error en puerta!
  3. Vean todo el código necesario solamente para usar una tabla!
  4. ¿Y qué ocurre si dentro de "el código del método" llamamos a otro procedimiento que nos cambia el área de trabajo o el puntero de registro? => Otro error!
  5. Cuando reusemos la tabla que había abierta y cambiemos el puntero del registro, por ejemplo con un Go Bottom, Scan, Locate, etc, ¿qué puede ocurrir al retornar al método llamador? => Otra posibilidad más de error!
  6. Estamos estableciendo un índice, con lo que al retornar al módulo llamador, si olvidamos poner el que había.... otro error!

Como se ve, no es buena idea usar esto en los procedimientos, ya que pueden ocurrir varios errores de distinto tipo, pero en todo caso requerirá controlar varias situaciones más que usando simplemente esto:

SELECT 0
USE MITABLA SHARED AGAIN ALIAS _MITABLA ORDER TAG INDICE
 
*...(el código del método)

USE IN (SELECT("_MITABLA"))

...¿a que es mucho menos código? :D

Pero además evitamos otros problemas:

- Al usar otro alias, el puntero de registro original no lo estamos modificando y nos ahorramos el tener que guardarlo y restaurarlo
- Abrimos la tabla con el tipo de acceso que nos interesa (Escritura, Solo-Lectura, etc)
- Abrimos la tabla con el índice que nos interesa, sin tener que restaurar el original
- ...y todo esto sin afectar a la tabla abierta anteriormente, ni al módulo llamador, ni utilizando un handle (manejador) de archivo extra, ya que Fox sabe si la tabla la tiene abierta en otro sitio, y reutiliza el handle.





Resumen


Las cláusulas SHARED, AGAIN y ALIAS permiten usar múltiples veces la misma tabla con diferentes nombres, además eso permite que cada sitio abra la tabla con el índice que requiera, tiene su propio puntero de registro que no será afectado por otros programas o métodos y no consume recursos extra, por lo que es la solución ideal en todos los casos.


Hasta la próxima! :D