Copiar registros de una tabla a otra en un solo movimiento

4,131 views
Skip to first unread message

Ultraton500

unread,
Dec 29, 2014, 11:39:44 AM12/29/14
to publice...@googlegroups.com
Buenas tardes a todos,
resulta que debo pasar miles de registros de una tabla .dbf libre a una tabla de Firebird y la única forma que conozco es recorrer la tabla con los registros y hacer un INSERT INTO... por cada uno.
Pero me queda la duda de si es esto una buena práctica o existe una manera de copiar todos los registros de una sola vez.
Les agradeceré cualquier comentario al respecto.

Saludos,
Javier.

Víctor Hugo Espínola Domínguez

unread,
Dec 29, 2014, 12:50:53 PM12/29/14
to publice...@googlegroups.com
Hola Javier

Lo puedes hacer con la clase cursor adapter, adjunto ejemplos.

Saludos,
Víctor.
Lambaré - Paraguay.
InsertCurAdapter._rar

Ultraton500

unread,
Dec 29, 2014, 1:13:43 PM12/29/14
to publice...@googlegroups.com
Hola Victor, gracias por la ayuda.
Ya los descargué y en breve me pongo a estudiarlo.

Saludos cordiales,
Javier.

Antonio Meza

unread,
Dec 29, 2014, 1:23:15 PM12/29/14
to publice...@googlegroups.com
También puedes usar FoxyDb 


Enviar datos de cursores a Firebird o Mysql en el caso de FoxyDb lo hace uno por uno usando SQLEXEC que es mejor que curosradapter

saludos

Ultraton500

unread,
Dec 29, 2014, 2:09:27 PM12/29/14
to publice...@googlegroups.com
Tengo pensado comenzar a probar FoxyDb la semana siguiente con la idea de implementarla. Así que, si mal no entiendo, sería entonces recorrer secuencialmente la tabla o cursor e insertar uno por uno usando SQLEXEC de FoxyDb.

Saludos y gracias,
Javier.

Antonio Meza

unread,
Dec 29, 2014, 2:26:24 PM12/29/14
to publice...@googlegroups.com
FoxyDb usa internamente SQLEXEC lo que tienes que hacer es conectarte a Firebird, luego obtener un cursor de la tabla de firebird que vas a actualizar, hacerla editable, luego abrir tu cursor o tabla de VFP, y enviar los registros de tu tabla de vfp al cursor de firebird, ya que hayas insertado todos los registros en el cursor de firebird mandas a actualizar el cursor, lo que hará FoxyDb internamente es enviar comandos Insert Into a través de SQLEXEC al servidor de firebird, y una vez terminado ese proceso enviar a guardar que realmente es confirmar la transacción en firebird.

Puede ver el ejemplo que hay en el blog el de la guía rápida, ahí están las funciones que necesitas, entendiendo tu problema lo que realmente vas hacer es una migración de datos no?

Espero haberme explicado.

saludos

Ultraton500

unread,
Dec 29, 2014, 4:43:12 PM12/29/14
to publice...@googlegroups.com
Así es Antonio, debo hacer una migración de datos pero primero debo eliminar todos los datos de la tabla, es decir, debo hacer una replicación.
Justamente empecé a revisar la documentación para ver cómo FoxyDb maneja las transacciones buscando si hay posibilidad de realizar el borrado y las inserciones en una sola para resguardar los datos originales por si algo detiene la replicación.
Gracias por la información, voy a ir leyendo y haciendo pruebas.

Saludos,
Javier.

mapner

unread,
Dec 29, 2014, 5:22:06 PM12/29/14
to publice...@googlegroups.com
Hola,

Te paso como ejemplo una pequeña clase para manipular datos en un servidor remoto ODBC.

- Obtienes los datos a actualizar desde el servidor remoto
- Realizas la actualización global en forma local
- Actualizas todos los cambios al servidor (La actualización de datos se hacer por medio de un Cursor Adapter).

Saludos

********************
* Ejemplo de como manipular datos desde un servidor ODBC remoto (en este caso Firebird)
* -- mapner --
*

* creo objeto
o = CREATEOBJECT('XCursor')

* String de conexión con FB
o.cStrCon = 'DRIVER=Firebird/InterBase(r) driver;UID=SYSDBA;PWD=masterkey;DBNAME=localhost:c:\data\xerp.fdb;'

* Alias de cursor local
o.cAlias = 'Cli'

* sentencia SQL
o.cSQL = 'select cli_id,cli_nombre, cli_direccion from gen_clientes'

* tabla actualizable
o.cDBTable = 'gen_clientes'

* campos actualizables
o.cUpdFldList = 'cli_id,cli_nombre, cli_direccion'

* claves primarias
o.cKeyFldList = 'cli_id'

* abro y traigo datos desde el servidor
IF o.OPEN()

* .... inserto datos ...
INSERT INTO Cli (cli_id, cli_nombre, cli_direccion) ;
  SELECT ClienteID, Nombre, Direccion FROM c:\dbf\clientes.dbf
* actualizo datos en el servidor
IF o.UPDATE()
MESSAGEBOX('Actualización Exitosa')
ENDIF
ENDIF

********************
*
DEFINE CLASS XCursor AS SESSION

cStrCon = '' 
nCon = -1
cAlias = ''
cSQL = ''
cDBTable = ''
cUpdFldList = ''
cKeyFldList = ''
lOpenConn = .F.
datasession = 1 && en este caso sesión predeterminada de datos
********************
*
PROCEDURE OPEN
LOCAL nRet, lOk 

THIS.GetConn()
nRet = SQLEXEC(THIS.nCon,THIS.cSQL,THIS.cAlias)
IF nRet >0
lOK = .T.
CURSORSETPROP("Buffering",5,THIS.cAlias)
ELSE
AERROR(aErx)
MESSAGEBOX('Error '+aErx[2])
lOK = .F.
ENDIF
THIS.CloseConn()
RETURN lOk
ENDPROC

********************
*
PROCEDURE UPDATE
LOCAL oCA , i, lOk, aErx[1]

THIS.GetConn()

oCA = NEWOBJECT("cursoradapter")
oCA.DATASOURCETYPE ="ODBC"
oCA.DATASOURCE = THIS.nCon
oCA.CURSORATTACH(THIS.cAlias)
oCA.TABLES = THIS.cDBTable
oCA.UPDATABLEFIELDLIST = THIS.cUpdFldList

oCA.UPDATENAMELIST = ''
FOR i=1 TO ALINES(aFld,oCA.UPDATABLEFIELDLIST,.T.,',')
oCA.UPDATENAMELIST = oCA.UPDATENAMELIST + aFld[i]+' '+oCA.TABLES+'.'+aFld[i]+' ,'
ENDFOR
oCA.UPDATENAMELIST = LEFT(oCA.UPDATENAMELIST,LEN(oCA.UPDATENAMELIST)-2)
oCA.KEYFIELDLIST = THIS.cKeyFldList
oCA.SENDUPDATES=.T.
oCA.WHERETYPE=1

lOk = TABLEUPDATE(1,.T.,THIS.cAlias)
IF !lOk
AERROR(aErx)
MESSAGEBOX('Error '+aErx[2])
ENDIF

oCA.CURSORDETACH()

THIS.CloseConn()

RETURN lOk

ENDPROC

********************
*
PROCEDURE GetConn
IF !THIS.lOpenConn 
THIS.nCon = SQLSTRINGCONNECT(THIS.cStrCon)
ENDIF
ENDPROC

********************
*
PROCEDURE CloseConn
IF !THIS.lOpenConn 
SQLDISCONNECT(THIS.nCon)
ENDIF
ENDPROC

ENDDEFINE

Víctor Hugo Espínola Domínguez

unread,
Dec 29, 2014, 6:36:59 PM12/29/14
to publice...@googlegroups.com
Hola Antonio


>Enviar datos de cursores a Firebird o Mysql en el caso de FoxyDb lo hace uno por uno usando >SQLEXEC que es mejor que curosradapter

Copiar una tabla de 15400 registros con 12 campos desde DBF a SqlServer tarda entre 11 y 15 segundos con cursor adapter y entre 180 y 190 segundos con SPT.

Saludos,
Víctor.
Lambaré - Paraguay.


Martin Paredes

unread,
Dec 29, 2014, 8:08:05 PM12/29/14
to publice...@googlegroups.com
si no quieres escribir nada de código, considera esto como alternativa, prueba haciendo esto :

1. exporta tu inf. (dbf) a excel formato csv delimitado con ','.
2. crea tu tabla en tu SGBD igual que tu estructura dbf.
3 importa los datos de excel hacia tu SGBD.
4 revisa inf.

ejemplo en postgresql con esta linea importo la inf. de excel hacia la tabla postgresql
COPY esquema.tabla FROM '/borrar/test.csv' DELIMITER ',' CSV;

así de sencillo.....

Salds....
Mapasac.
General Escobedo, N.L.Mexico

Florencio Beltran de Heredia

unread,
Dec 30, 2014, 6:11:01 AM12/30/14
to publice...@googlegroups.com
Considera usar la funciones Scatter y Gather que con la clausela "name" crean un objeto de un registro de cualquier tabla. Las instrucciones para copiar una tabla completa serían las siguientes. Esto no te llevrá más de un momento, por muy grande que sea la tabla original.

sele tablaoriginal
scan
     scatter name miobjeto     && crea un objeto (miobjeto con los datos del registro de la tabla a la que estamos apuntando. Si hay campos memo, la instrucción sería: scatter name mibojeto memo
     sele tabladestino
gather name mibojeto

Florencio Beltran de Heredia

unread,
Dec 30, 2014, 6:14:20 AM12/30/14
to publice...@googlegroups.com
Perdon por mi publicación anterior. Se publicó sin estar finalizada la contestación.

La instrucción completa sería:

sele tablaoriginal
scan
   scatter name miobjeto
   sele tabladestino
   gather name miobjeto
   sele tablaoriginal
endscan


Saludos

Antonio Meza

unread,
Dec 30, 2014, 12:22:15 PM12/30/14
to publice...@googlegroups.com
Posiblemente tienes mal aplicada las transacciones con SPT y por eso el tiempo, porque en varios foros dicen que es mejor SPT que cursor adapter.

A demás SPT también puedes vaciar un cursor o varios registros de una sola pasada, el detalle es que al hacer eso pierdes algunas características y como normalmente eso se hace una sola vez cuando estas migrando un sistema, es mejor contar con las características que usaras continuamente que perderles para acelerar un proceso que harás una sola vez.

saludos
Antonio Meza

Antonio Meza

unread,
Dec 30, 2014, 12:27:26 PM12/30/14
to publice...@googlegroups.com
Hola Florencio!!

El codigo que indicas no le funciona ya que va a exportar los datos a Firebird.

saludos

Antonio Meza

unread,
Dec 30, 2014, 12:30:58 PM12/30/14
to publice...@googlegroups.com
Antes que nada es mejor hacer una copia de tu base de datos de firebird, y a demas correr el proceso en una copia para ver que funcione tu migracion y ya que estes seguro realizar el proceso en los datos originales.

En cuanto a las transacciones no tienes que preocuparte por ellas, FoxyDb las meneja por ti, solo tienes que realizar los procesos como estan en la ayuda.

Pero como todo también lo puedes hacer manual en FoxyDb, 

saludos

mapner

unread,
Dec 30, 2014, 2:50:30 PM12/30/14
to publice...@googlegroups.com
Otra alternativa para migrar datos a Firebird es usar Tables External Files
Esto permite tener pseudo-tablas enganchadas con un archivo de texto externo y poder manipularlo dentro del servidor de BD. 
En el caso de DBFs se copian a tipo SDF texto plano para luego vincularlo en FB con Table External File.
Recuerdo hace algunos años haber migrado sistemas Clipper a FB. Con este método el tiempo de proceso bajaba considerablemente.

algunos links 






El lunes, 29 de diciembre de 2014 13:39:44 UTC-3, Ultraton500 escribió:

mapner

unread,
Dec 30, 2014, 4:02:33 PM12/30/14
to publice...@googlegroups.com
Complementando el tema de migrar datos a Firebird por el método de Table External File les paso una rutina VFP que automatiza todo el proceso 

Ej. de uso:

USE C:\DBF\CLIENTES ALIAS CLI IN 0
nCon = SQLSTRINGCONNECT(.....)
ImportFBExt('Cli','Clientes',nCon,'C:\DBEXT\') 

Saludos
 
******************************
* ImportFBExt
* -- mapner --
* Importador Para Base de Datos Firebird desde un cursor VFP
*
* Dado un cursor local VFP con la misma estructura de campos que una tabla remota Firebird
* lo importa en el servidor usando Table External File
* cArea : Cursor Local
* cTable: Tabla Firebird
* nCon : Conexión ya establecida con BD Firebird
* cDirExt : Directorio del servidor donde se grabará el archivo plano a importar
*
* ej.
* USE C:\DBF\CLIENTES ALIAS CLI IN 0
* nCon = SQLSTRINGCONNECT(.....)
* ImportFBExt('Cli','Clientes',nCon,'C:\DBEXT\') 
*
PROCEDURE ImportFBExt
LPARAMETERS cArea,cTable,nCon,cDirExt
LOCAL nF,cSQL,i, cExtFile, cFieldList1, cFieldList2, aFlds[1], cCmd 

cExtFile = LOWER(STRTRAN(cDirExt+cArea,'v_',''))+'.txt'

cFieldList1 = ""
cFieldList2 = ""
cSQL = "DROP TABLE "+cTable+"_TXT"
ExecSQL(nCon,cSQL,'',.T.)
SELECT (cArea)
* Crea el comando SQL FB para crear la Table External File
cSQL = "CREATE TABLE "+cTable+"_TXT EXTERNAL FILE '"+cExtFile+"' ( "+CHR(13)
FOR i=1 TO AFIELDS(aFlds)
DO CASE
CASE aFlds[i,2] = "I"
nLen = 11
CASE aFlds[i,2] $ "D"
nLen = 8
CASE aFlds[i,2] = "N"
nLen = aFlds[i,3]
CASE aFlds[i,2] $ "T"
LOOP
OTHERWISE
nLen = aFlds[i,3]
ENDCASE
cSQL = cSQL + aFlds[i,1] +" CHAR("+ALLTRIM(STR(nLen))+") "
cFieldList1 = cFieldList1 + "CaFlds("+aFlds[i,1] + " AS CHAR("+ALLTRIM(STR(nLen))+")) "
cFieldList2 = cFieldList2 + aFlds[i,1]
cSQL = cSQL +", "+CHR(13)
cFieldList1 = cFieldList1 +", "
cFieldList2 = cFieldList2 +", "
ENDFOR

cSQL  = LEFT(cSQL,LEN(cSQL)-3)
cFieldList1  = LEFT(cFieldList1,LEN(cFieldList1)-2)
cFieldList2 = LEFT(cFieldList2,LEN(cFieldList2)-2)

cSQL = cSQL +" , "+CHR(13)+" NEWLINE__x__ char(2)"
cSQL = cSQL + ");"+CHR(13)+CHR(13)

* A partir del cursor local genera el archivo plano SDF
cCmd = "COPY FIELDS "+cFieldList2+" TO (cExtFile) SDF"    && copio al txt
&cCmd
ExecSQL(nCon,cSQL)
*  Creo el comando SQL FB para insertar en la tabla FB destino desde la tabla externa origen
cSQL = "INSERT INTO "+cTable+"  ("+cFieldList2+") SELECT "+cFieldList1+" FROM "+cTable+"_TXT;"+CHR(13)+CHR(13)
ExecSQL(nCon,cSQL)
* La correspondencia de tipos es: 
* INTEGER como CHAR(11); SMALLINT como CHAR(6); DATE como
* CHAR(8), DECIMAL(8,2) como CHAR(13).

* elimino la tabla externa
cSQL = "DROP TABLE "+cTable+"_TXT "
ExecSQL(nCon,cSQL)

ENDPROC

********************
*
PROCEDURE ExecSQL
LPARAMETERS nCon, cSQL, cArea, lNoCheck

IF !EMPTY(cArea)
nRet = SQLExec(nCon,cSQL,cArea)
ELSE
nRet = SQLExec(nCon,cSQL)
ENDIF 

SQLSETPROP(nCon,"Transactions",2) 
IF nRet < 1 AND !lNoCheck
AERROR(aErx)
MESSAGEBOX('Error '+aErx[2])
SQLROLLBACK(nCon)
CANCEL 
ENDIF
SQLCOMMIT(nCon)

ENDPROC

Víctor Hugo Espínola Domínguez

unread,
Dec 30, 2014, 6:40:42 PM12/30/14
to publice...@googlegroups.com
Hola Florencio

>sele tablaoriginal
>scan
>   scatter name miobjeto
>   sele tabladestino
>   gather name miobjeto
>   sele tablaoriginal
>endscan

Se puede simplificar a:

sele tablaoriginal
scan
   scatter name miobjeto
   sele tabladestino
   gather name miobjeto
endscan

El select tablaoriginal antes del endscan no es necesario, el scan lo hace automáticamente.

Además estas instruciones son equivalentes a:

Select TablaDestino
Append From TablaOrigen

Tener en cuenta que si la tabla de origen tiene una clave primaria auto incremental estos código fallarán!

Saludos,
Víctor.
Lambaré - Paraguay.

Víctor Hugo Espínola Domínguez

unread,
Dec 30, 2014, 7:07:29 PM12/30/14
to publice...@googlegroups.com
Hola Antonio

>Posiblemente tienes mal aplicada las transacciones con SPT y por eso el tiempo, porque en varios foros dicen que >es mejor SPT que cursor adapter.

La función TABLEUPDATE de un cursor actualizable sea o no de la clase cursor adapter debería ser más veloz que un ciclo de INSERT INTO dentro de SCAN/ENDSCAN o DO WHILE/ENDDO, pero no me creas, haz las pruebas pertinentes.

No se debería comparar SPT con Cursor adapter, hay que conocer ambas técnicas y sacarles el máximo provecho. Con Cursoradapter combinado con Xml adapter se tiene una potencia y ahorro de líneas de código impresionantes.

>A demás SPT también puedes vaciar un cursor o varios registros de una sola pasada

Me interesa el tema, podrías dar un ejemplo o por lo menos explicarlo?

Saludos,
Víctor.
Lambaré - Paraguay.

Luis Salazar

unread,
Jan 1, 2015, 10:09:16 AM1/1/15
to publice...@googlegroups.com
En realidad yo por lo general NO me preocupo si un proceso tarda 10 o mas
segundos si lo ejecuto de una forma u otra .. Primero si la frecuencia del
proceso es de baja;  es decir una vez a la semana o una vez al
mes con o con un Cierre Diario .. NO se a lo mejor me tarda mas tiempo averiguar
programar, probar etc cuanto reducir esa  cantidad de 10 segundos
...Al asegurarme que el proceso se ejecuto correctamente estoy tranquilo ..
.... No se en esos 10 segundos NO creo voy a arreglar el mundo ..
EN realidad 15000 registros no me parecen un volumen tan alto ..  NO se  digooo  yoooo
Mis disculpas ..

Antonio Meza

unread,
Jan 2, 2015, 12:02:30 PM1/2/15
to publice...@googlegroups.com
En la ayuda de VFP del comando SQLEXEC viene como enviar múltiples comandos SQL separados por un ";" punto y coma, como lo harías desde algún programa SQL, normalmente SQLEXEC solo permite enviar un comando SQL a la vez, pero modificando algunos parámetros SQLSETPROP puedes enviar varios comandos y recuperar los resultados con SQLMORERESULTS, hace tiempo hice pruebas pero al hacer eso ya no puedes obtener el valor devuelto por aCountInfo

SQLEXEC(nStatementHandle [, cSQLCommand [, cCursorName[, aCountInfo]]])

aCountInfo
Specifies the name of the array to populate with row count information. If the array doesn’t exist, it is created. The array has two columns: 1 – Alias, 2 –Count. 

Muy útil por ejemplo para saber cuantos registros devolvió una consulta Select, o si enviaste un Update puedes saber cuando registros se actualizaron, al igual si envías un Delete, por ejemplo si envías un Update para guardar el valor de un campo X y resulta que el valor del campo X es el mismo valor que tiene la base de datos, entonces Sqlexec te devolverá 1 que si ejecuto el Update, pero la cantidad de registros modificados sera 0 ya que los valores eran iguales y por lo tanto el servidor de base de datos no ejecuto el update, en mi caso le aviso al usuario que "no se han realizado cambios para guardar", y otros programas normalmente dirán que "se guardaron los cambios"

CursorAdapter vs SPT

A mi entender la principal diferencia es que CursorAdapter es parecido a Vistas Remotas pero con asteroides, es decir ambos te devuelven un cursor actualizable, y SPT no, sin embargo con SPT puedes de igual forma crear un cursor actualizable, por lo tanto CA, VR y SPT usan los mismos comandos y en esencia son iguales, y el nivel de optimizacion hace la diferencia entre uno y otro, por ejemplo VR (Vistas Remotas) no puedes usar Store Procedure que si puedes usar en los otros dos y ahí ya vas ganando mucho rendimiento.

Con CA puedes usar ADO / OLEDB / ODBC, con SPT creo usas solo ODBC, OledDb pues ya se quedo en el pasado, entonces volvemos a lo mismo que es mejor ADO o ODBC.

Hace años desarrolle una clase DbVfp que usaba SPT y te devolvía un cursor actualizable, pero luego me encontré con un cliente que su servidor Mysql estaba en un Hosting por lo que era mejor conectarse y desconectarse, y los cursores actualizables no puedes hacer eso, entonces CursorAdapter usa cursores actualizables, no se si puedas hacer eso de que tengas el cursor en memoria listo para guardar, desconectes del servidor y te vuelvas a conectar y puedas enviar a guardar?

Luego el uso de las transacciones cada motor de base de datos tiene sus diferencias, a demás un simple Select ejecutado dentro de una transacción de solo lectura es mucho mas rápido que si lo ejecutas dentro de una transacción de lectura y escritura, y con CursorAdapter puedes manipular eso?

Hay muchas cosas pero al final todo termina con la misma palabra el nivel de Optimizacion que te pueda permitir uno de otro, a mi punto de vista con SPT tienes el control, y mas trabajo, con CA tienes menos código pero menos control.

saludos
Antonio Meza



saludos
Antonio Meza

Ultraton500

unread,
Jan 2, 2015, 2:51:09 PM1/2/15
to publice...@googlegroups.com
Buenas a todos, espero que hayan comenzado el nuevo año de la mejor manera y que sea un gran año para todos.
El día 30, antes de responder las nuevas respuestas quise averiguar qué se podría hacer al respecto con las tablas externas de Firebird cuando a todo esto me encuentro con lo publicado por mapner.
La migración masiva de datos mediante archivo de texto, independientemente de la efectividad de las demás alternativas, es particularmente interesante ya que se trataría del procedimiento recomendado por Firebird para realizar esta tarea.
Al igual que likiliki no me preocupo por unos segundos mas que pueda demorar un proceso secuencial, pero como en el blog de Walter Ojeda se habla de "horas y horas" inevitablemente es algo que creo que hay que considerar.
De todas formas he leído con atención y tomado nota de cada una de las respuestas y estoy mas que satisfecho por la ayuda recibida y por los datos interesantes que se hablaron en este tema (al menos para mi).

Muchas gracias a todos por sus colaboraciones como siempre.

Saludos cordiales,
Javier.

mapner

unread,
Jan 2, 2015, 3:15:52 PM1/2/15
to publice...@googlegroups.com
Amigo Ultraton500,

Permite decirte que la migración con Table External File en Fifebird la he utilizado para migrar tablas de unos 200000 registros y el tiempo usando este método es mucho mejor que cualquier otra variante. Para el caso de los 200000 registros la grabación tardaba unos 10 minutos con una PC bastante arcaica,hoy en día con un I7 el tiempo debería bajar notoriamente.
Saludos y buen año!

mapner

unread,
Jan 2, 2015, 3:21:46 PM1/2/15
to publice...@googlegroups.com
Aclaro, es Firebird, acá en Argentina Fife es otra cosa... :)
Saludos

Víctor Hugo Espínola Domínguez

unread,
Jan 2, 2015, 5:06:34 PM1/2/15
to publice...@googlegroups.com
FifeBird: lunfardo "fife" + inglés "bird"(Pavito?)

Saludos,
Víctor.
Lambaré - Paraguay.

Víctor Hugo Espínola Domínguez

unread,
Jan 2, 2015, 6:03:58 PM1/2/15
to publice...@googlegroups.com
Hola Antonio


>En la ayuda de VFP del comando SQLEXEC viene como enviar múltiples comandos SQL separados >por un ";" punto y coma, como lo harías desde algún programa SQL, normalmente SQLEXEC solo >permite enviar un comando SQL a la vez, pero modificando algunos parámetros SQLSETPROP >puedes enviar varios comandos y recuperar los resultados con SQLMORERESULTS

Cuales serían los comandos separados por ";" para insertar los registros de un cursor en una tabla remota?

No puedo entenderlo aún, resaca de las fiestas probablemente ;-).

Saludos,
Víctor.
Lambaré - Paraguay.

Ultraton500

unread,
Jan 5, 2015, 12:26:38 AM1/5/15
to publice...@googlegroups.com
Qué tal mapner...
antes que nada quiero agradecerte por compartir la rutina. Estoy entusiasmado tratando de hacerla funcionar pero en VFP la línea MESSAGEBOX('Error '+aErx[2]) produce el error " File 'aerx.prg' does not exist. " y esto ocurre cuando AERROR() es igual a cero.
Supongo que a esto se refiere Walter en su blog al decir que ante un error de inserción Firebird no dará información suficiente al respecto y, aunque no sé por qué, es justamente en la inserción en donde surge el error.
Hasta donde me da la cabeza he entendido bastante del funcionamiento de la rutina pero una de las cosas que no puedo entender es qué función cumple "CaFlds" dentro del INSERT, por ej. 

INSERT INTO clientes (...   ...) SELECT CaFlds(CODIGO AS CHAR(6)), CaFlds(NOMBRE AS CHAR(110)), ...

Cualquier cosa que se te ocurra que pueda estar haciendo mal te agradecería si me lo comentas.

Saludos cordiales,
Javier.

mapner

unread,
Jan 5, 2015, 10:23:33 AM1/5/15
to publice...@googlegroups.com
Hola Ultraton500,

si, algunos BUGS de transcripción.
Acá va nuevamente el código con las correcciones sobre los puntos mencionados

saludos

cFieldList1 = cFieldList1 + "CAST("+aFlds[i,1] + " AS CHAR("+ALLTRIM(STR(nLen))+")) "

SQLSETPROP(nCon,"Transactions",2) 

IF !EMPTY(cArea)
nRet = SQLExec(nCon,cSQL,cArea)
ELSE
nRet = SQLExec(nCon,cSQL)
ENDIF 
IF nRet < 1 AND !lNoCheck
AERROR(aErx)
MESSAGEBOX('Error '+aErx[2])
SQLROLLBACK(nCon)
CANCEL 
ENDIF
SQLCOMMIT(nCon)

ENDPROC

Antonio Meza

unread,
Jan 5, 2015, 12:19:48 PM1/5/15
to publice...@googlegroups.com
Hola Victor!!

Como te mencione anteriormente, hace un tiempo realice pruebas y al final logre enviar multiples comandos sql desde SqlExec, pero como no me intereso al final no guarde el código jeje y me costo mucho entender como lograrlo, solo recuerdo que hay que mover varias propiedades con SqlSetProp, y a parte agregarle un codigo Chr(13) al final de cada instrucción.

algo asi:

txtSql = "Insert into tabla (campo1) values ("valor1") + chr(13)
txtSql = txtSql = "Insert into tabla (campo1) values ("valor1") + chr(13)
txtSql = txtSql = "Insert into tabla (campo1) values ("valor1") + chr(13)

Sqlexec(hanled,txtsql)

Pero de que se puede si jeje

saludos!!

Víctor Hugo Espínola Domínguez

unread,
Jan 5, 2015, 3:25:38 PM1/5/15
to publice...@googlegroups.com
Hola Antonio

El ejemplo que muestras entiendo que es para insertar varios registros conociendo de antemano los valores de los campos de cada registro.

Mi pregunta era de cómo insertar los datos de un cursor o tabla mediante SPT.

Saludos y Feliz Año nuevo,
Víctor.
Lambaré - Paraguay.

Ultraton500

unread,
Jan 7, 2015, 2:32:00 PM1/7/15
to publice...@googlegroups.com
Ahora sí mapner!! esta rutina funciona de maravillas.
Por si a alguien le sirve el dato les comento que al ejecutarla obtenía el error

" Error Connectivity error: [ODBC Firebird Driver][Firebird]Access to external file "C:\DBEXT\CUR_CLIENTES.TXT" is denied by server administrator "

Para evitar este error se debe establecer el parámetro " ExternalFileAccess = Full " ó " ExternalFileAccess = Restricted C:\DBEXT " el archivo firebird.conf, siendo la segunda opción la mas apropiada por razones de seguridad.
Nuevamente gracias por compartir.

Saludos cordiales,
Javier.

mapner

unread,
Jan 7, 2015, 2:40:47 PM1/7/15
to publice...@googlegroups.com
Ultraton500,

Me alegro que te haya servido la rutina. Por curiosidad, que volumen de datos estás migrando y que perfomance de tiempo se toma el servidor en la importación de los datos?
Como te comenté anteriormente, esto lo hice hace algún tiempo y con las máquinas actuales debe tener mejores marcas que entonces.

Saludos!

Ultraton500

unread,
Jan 8, 2015, 4:12:46 PM1/8/15
to publice...@googlegroups.com
Hola mapner, en realidad lo que estoy migrando ahora es una miseria (menos de 15000 registros) y por eso no tarda nada. Pero hice algunas pruebas.

La tabla .dbf que migro hacia Firebird tiene solo 9 campos y la siguiente estructura:

numeric          10
character        20
character        20
numeric          10,2
numeric          10,2
numeric          10,2
numeric          10,2
numeric          6,2
numeric          5,2

Y los tiempos son estos:

1.000.000 de registros: 16 seg.
2.000.000 de registros: 34 seg.
3.000.000 de registros: 53 seg.
4.000.000 de registros: 1 min. 27 seg.
5.000.000 de registros: 1 min. 47 seg..
6.000.000 de registros: 2 min. 41 seg.

Hay que tener en cuenta que las pruebas son sobre un servidor local y que la máquina en donde hice las pruebas es bastante rápida y tiene bastante memoria:

Core i7-3770 3.40Ghz.

Memoria 16Gb
  Total:       16346
  En caché:    4237
  Disponible:  12035
  Libre:       7978

Windows 7 64bits


Aunque dan una buena referencia, los tiempos tampoco son siempre iguales, por ejemplo volví a migrar 6.000.000 de registros varias veces mas y estos son los tiempos que le tomó a cada una:

la 2da. vez 2 min. 5 seg.
la 3ra. vez 1 min. 58 seg.
la 4ta. vez 2 min. 6 seg.
(reinicié la máquina)
la 5ta. vez 1 min. 38. seg.
la 6ta. vez 2 min. 14 seg..

No sé si es para tener en cuenta pero lo que hice para incrementar la cantidad de registros en la tabla .dbf fue ir haciendo un append from de la misma tabla. No creo que el hecho de tener muchas veces las mismas filas influya en con los tiempos de respuesta.
Otro dato es que vacié la tabla de Firebird antes de cada prueba.
No tengo mas tiempo, pero en cuanto pueda voy a ver qué pasa con 8.000.000 de registros porque anoche hice la prueba y luego de dos horas vfp seguía sin responder y el archivo .txt, que superó los 500Mb, estaba en blanco y no respondía cuando lo abrí.
Espero te sirva como referencia.

Saludos cordiales,
Javier.

mapner

unread,
Jan 8, 2015, 4:28:04 PM1/8/15
to publice...@googlegroups.com
Javier Ultraton500,

Por lo que veo son muy buenos tiempos para la cantidad de registros que estás referenciando.
Esta modalidad de importación con Table External File siempre deberá ser ejecutado dentro del propio servidor ya que Firebird no admite que el file externo esté en una unidad de red, siempre debe residir en discos o unidades locales del mismo servidor.
Lo de las diferencias de velocidad se puede deber al uso que le da el FB a los datos buffereados. Sucede a menudo que la primera consulta tarde un tiempo y las subsecuentes tarden un tanto menos.
Nuevamente, me alegro que te haya servido la rutina.

Saludos
Reply all
Reply to author
Forward
0 new messages