In order for this site to work correctly we need to store a small file (called a cookie) on your computer. Most every site in the world does this, however since the 25th of May 2011, by law we have to get your permission first. Please abandon the forum if you disagree.

Para que este foro funcione correctamente es necesario guardar un pequeño fichero (llamado cookie) en su ordenador. La mayoría de los sitios de Internet lo hacen, no obstante desde el 25 de Marzo de 2011 y por ley, necesitamos de su permiso con antelación. Abandone este foro si no está conforme.

Sincronizar tablas Maridb

SQL databases
Responder
Avatar de Usuario
gerencia
Mensajes: 282
Registrado: Sab Jun 20, 2009 6:01 pm

Sincronizar tablas Maridb

Mensaje por gerencia »

Hola a todos,

Alguien ha hecho algo de forma manual que permita sincronizar 2 tablas una local y otra en la nube de
Mariadb ?.

La idea es mantener actualizada una información en la nube de lo que ocurre con la tabla local, no se puede usar
la opción de configurar los servidores para que se hagan automáticas por lo que no disponemos de configurar el servidor de
Mariadb en la nube, por lo que deberá hacerse de forma manual .

Si alguno ha desarrollado algo me vendría bien un poco de ayuda.

Saludos a todos,
Jose David Angulo Urzola
Auditoria y Sistemas Ltda.
Cartagena-Colombia
Avatar de Usuario
bingen
Mensajes: 564
Registrado: Lun Jul 07, 2014 8:17 pm
Ubicación: Bilbao
Contactar:

Re: Sincronizar tablas Maridb

Mensaje por bingen »

Buenas, nosotros lo hemos desarrollado a través de un Webdatasouce y mantenemos sincronizado el escritorio con la Web. Espero que este código te sirva de orientación, sino ponte en contacto conmigo.

//En caso de trabajar con un WebDataSource externo y existir en él una tabla con el mismo nombre
//replica los registros indicados en la Web por su Id

Function SynchronizeWebExt(cOrigen, cDestino, xId, lDelete, lTruncateBefore)

Local lControl:=.F., aCampos:={}, aDatos:={}, aCamposOrigen:={}, nCampo:=0, cValores:="", aValores :={}
Local lSuccess:=.T.

Default cDestino to cOrigen
Default lDelete To .F. //Si el procedimiento es de borrado en lugar de ser inserción/edición
Default lTruncateBefore To .F. //Antes de realizar la operaci´çon se vacia la totalidad de la tabla

cOrigen := SubStr(cOrigen,At(".",cOrigen)+1)
cDestino := SubStr(cDestino ,At(".",cDestino )+1)


//Si no se ha configurado el WebDataSource Externo esto no vale para nada
If Appdata:oWebDataSourceExt=Nil
Return .F.
Endif

//El xId tiene que venir con algo si no no hay que hacer nada
if Vacio(xId)
LogDebug("No se puede sincronizar un contenido de xId Vacio para "+cOrigen+" >>> "+cDestino+" en la BBDD "+Appdata:cWebDatabaseExt)
Return .F.
Endif

//Tiene que existir el origen en la BBDD de trabajo o en la de control
if Vacio(Appdata:oSqlSource:GetTables(cOrigen))
lControl:=.T.
if Vacio(Appdata:oControlDb:GetTables(cOrigen))
LogDebug("No se puede encontrar el archivo de origen "+cOrigen+" para la sincronización Web")
Return .F.
Endif
Endif

//Abrir la BBDD de la Web externa si no lo está
If !Appdata:oWebDataSourceExt:lConnected()
Appdata:oWebDataSourceExt:lConnected:=.T.
Endif

//Tiene que existir el destino en la BBDD de la Web externa
Appdata:oWebDataSourceExt:lConnected:=.T.
If Vacio(Appdata:oWebDataSourceExt:GetTables(cDestino))
Appdata:oWebDataSourceExt:lConnected:=.F.
LogDebug("No se puede encontrar el archivo de destino "+cDestino+" para la sincronización Web")
Return .F.
Endif


//Lista de campos del origen y del destino para ver que hay que sincronizar
aCamposOrigen:=IF(!lControl,Appdata:oSqlSource:QueryArray("describe "+cOrigen),Appdata:oControlDb:QueryArray("describe "+cOrigen))
aCampos:=Appdata:oWebDataSourceExt:QueryArray("describe "+cDestino)
For nCampo:=1 to Len(aCampos)
If AScan(aCamposOrigen,{|x| Lower(x[1])==Lower(aCampos[nCampo,1]) })=0
HB_ADel(aCampos,nCampo,.T.)
nCampo--
Endif
Next

//El Id siempre tiene que ser un array y monodimensional
If ValType(xId)<>"A"
xId:={xId}
Endif

//Añadir o actualizar registros en la web
Appdata:oWebDataSourceExt:BeginTrans()
If !lDelete
//Si se ha pedido lTruncateBefore vaciar la tabla antes de empezar
If lTruncateBefore
Appdata:oWebDataSourceExt:Execute("truncate "+cDestino)
Endif

If !lControl
aDatos:=Appdata:oSqlSource:QueryArray("select "+ArrayToChar(aCampos,",",1)+" from "+cOrigen+" where id in("+ArrayToChar(xId,",")+")" )
Else
aDatos:=Appdata:oControlDb:QueryArray("select "+ArrayToChar(aCampos,",",1)+" from "+cOrigen+" where id in("+ArrayToChar(xId,",")+")" )
Endif

Appdata:oWebDataSourceExt:BeginTrans()

For Each aValores in aDatos
cValores+=" ("+Concat(aValores,,.T.)+"), "
If Len(cValores)>60000
lSuccess := IF(!Appdata:oWebDataSourceExt:Execute("replace into "+cDestino+" ("+ArrayToChar(aCampos,",",1)+") Values "+HB_StrShrink(cValores,2)) .Or. !lSuccess, .F.,.T.)
cValores:=""
If Mod(aValores:__enumIndex,8)=0 //Para prevenir el tamaño máximo de la instrucción Http que se envia al servidor Apache via WebDataSource
Appdata:oWebDataSourceExt:CommitTrans()
Appdata:oWebDataSourceExt:BeginTrans()
Endif
Endif
If !lSuccess
Exit
Endif
Next
lSuccess := IF(!Appdata:oWebDataSourceExt:Execute("replace into "+cDestino+" ("+ArrayToChar(aCampos,",",1)+") Values "+HB_StrShrink(cValores,2)) .Or. !lSuccess, .F.,.T.)
Else //Eliminar registros en la web
lSuccess := Appdata:oWebDataSourceExt:Execute("delete from "+cDestino+" where id in("+ArrayToChar(xId,",")+")")
Endif

If !lSuccess
Appdata:oWebDataSourceExt:RollBackTrans()
Else
Appdata:oWebDataSourceExt:CommitTrans()
Endif

//Cerrar la BBDD de la Web externa
Appdata:oWebDataSourceExt:lConnected:=.F.

Return lSuccess
//------------------------------------------------------------------------------
BiSoft Desarrollo de software profesional
http://www.bisoft.es
Avatar de Usuario
ignacio
Site Admin
Mensajes: 9243
Registrado: Lun Abr 06, 2015 8:00 pm
Ubicación: Madrid, Spain
Contactar:

Re: Sincronizar tablas Maridb

Mensaje por ignacio »

Buenas noches,

Simplemente utilice los evento del dataset local para actualizar todo en remoto

Saludos
Ignacio Ortiz de Zúñiga
[Equipo de Xailer / Xailer team]
https://www.xailer.com
Avatar de Usuario
gerencia
Mensajes: 282
Registrado: Sab Jun 20, 2009 6:01 pm

Re: Sincronizar tablas Maridb

Mensaje por gerencia »

Bingen,
muchas gracias por tu aporte.

Podrias publicar la funcioón Concat() que estas usando.



bingen escribió: Sab Ene 08, 2022 7:38 am Buenas, nosotros lo hemos desarrollado a través de un Webdatasouce y mantenemos sincronizado el escritorio con la Web. Espero que este código te sirva de orientación, sino ponte en contacto conmigo.

//En caso de trabajar con un WebDataSource externo y existir en él una tabla con el mismo nombre
//replica los registros indicados en la Web por su Id

Function SynchronizeWebExt(cOrigen, cDestino, xId, lDelete, lTruncateBefore)

Local lControl:=.F., aCampos:={}, aDatos:={}, aCamposOrigen:={}, nCampo:=0, cValores:="", aValores :={}
Local lSuccess:=.T.

Default cDestino to cOrigen
Default lDelete To .F. //Si el procedimiento es de borrado en lugar de ser inserción/edición
Default lTruncateBefore To .F. //Antes de realizar la operaci´çon se vacia la totalidad de la tabla

cOrigen := SubStr(cOrigen,At(".",cOrigen)+1)
cDestino := SubStr(cDestino ,At(".",cDestino )+1)


//Si no se ha configurado el WebDataSource Externo esto no vale para nada
If Appdata:oWebDataSourceExt=Nil
Return .F.
Endif

//El xId tiene que venir con algo si no no hay que hacer nada
if Vacio(xId)
LogDebug("No se puede sincronizar un contenido de xId Vacio para "+cOrigen+" >>> "+cDestino+" en la BBDD "+Appdata:cWebDatabaseExt)
Return .F.
Endif

//Tiene que existir el origen en la BBDD de trabajo o en la de control
if Vacio(Appdata:oSqlSource:GetTables(cOrigen))
lControl:=.T.
if Vacio(Appdata:oControlDb:GetTables(cOrigen))
LogDebug("No se puede encontrar el archivo de origen "+cOrigen+" para la sincronización Web")
Return .F.
Endif
Endif

//Abrir la BBDD de la Web externa si no lo está
If !Appdata:oWebDataSourceExt:lConnected()
Appdata:oWebDataSourceExt:lConnected:=.T.
Endif

//Tiene que existir el destino en la BBDD de la Web externa
Appdata:oWebDataSourceExt:lConnected:=.T.
If Vacio(Appdata:oWebDataSourceExt:GetTables(cDestino))
Appdata:oWebDataSourceExt:lConnected:=.F.
LogDebug("No se puede encontrar el archivo de destino "+cDestino+" para la sincronización Web")
Return .F.
Endif


//Lista de campos del origen y del destino para ver que hay que sincronizar
aCamposOrigen:=IF(!lControl,Appdata:oSqlSource:QueryArray("describe "+cOrigen),Appdata:oControlDb:QueryArray("describe "+cOrigen))
aCampos:=Appdata:oWebDataSourceExt:QueryArray("describe "+cDestino)
For nCampo:=1 to Len(aCampos)
If AScan(aCamposOrigen,{|x| Lower(x[1])==Lower(aCampos[nCampo,1]) })=0
HB_ADel(aCampos,nCampo,.T.)
nCampo--
Endif
Next

//El Id siempre tiene que ser un array y monodimensional
If ValType(xId)<>"A"
xId:={xId}
Endif

//Añadir o actualizar registros en la web
Appdata:oWebDataSourceExt:BeginTrans()
If !lDelete
//Si se ha pedido lTruncateBefore vaciar la tabla antes de empezar
If lTruncateBefore
Appdata:oWebDataSourceExt:Execute("truncate "+cDestino)
Endif

If !lControl
aDatos:=Appdata:oSqlSource:QueryArray("select "+ArrayToChar(aCampos,",",1)+" from "+cOrigen+" where id in("+ArrayToChar(xId,",")+")" )
Else
aDatos:=Appdata:oControlDb:QueryArray("select "+ArrayToChar(aCampos,",",1)+" from "+cOrigen+" where id in("+ArrayToChar(xId,",")+")" )
Endif

Appdata:oWebDataSourceExt:BeginTrans()

For Each aValores in aDatos
cValores+=" ("+Concat(aValores,,.T.)+"), "
If Len(cValores)>60000
lSuccess := IF(!Appdata:oWebDataSourceExt:Execute("replace into "+cDestino+" ("+ArrayToChar(aCampos,",",1)+") Values "+HB_StrShrink(cValores,2)) .Or. !lSuccess, .F.,.T.)
cValores:=""
If Mod(aValores:__enumIndex,8)=0 //Para prevenir el tamaño máximo de la instrucción Http que se envia al servidor Apache via WebDataSource
Appdata:oWebDataSourceExt:CommitTrans()
Appdata:oWebDataSourceExt:BeginTrans()
Endif
Endif
If !lSuccess
Exit
Endif
Next
lSuccess := IF(!Appdata:oWebDataSourceExt:Execute("replace into "+cDestino+" ("+ArrayToChar(aCampos,",",1)+") Values "+HB_StrShrink(cValores,2)) .Or. !lSuccess, .F.,.T.)
Else //Eliminar registros en la web
lSuccess := Appdata:oWebDataSourceExt:Execute("delete from "+cDestino+" where id in("+ArrayToChar(xId,",")+")")
Endif

If !lSuccess
Appdata:oWebDataSourceExt:RollBackTrans()
Else
Appdata:oWebDataSourceExt:CommitTrans()
Endif

//Cerrar la BBDD de la Web externa
Appdata:oWebDataSourceExt:lConnected:=.F.

Return lSuccess
//------------------------------------------------------------------------------
Jose David Angulo Urzola
Auditoria y Sistemas Ltda.
Cartagena-Colombia
Avatar de Usuario
bingen
Mensajes: 564
Registrado: Lun Jul 07, 2014 8:17 pm
Ubicación: Bilbao
Contactar:

Re: Sincronizar tablas Maridb

Mensaje por bingen »

Aqui la tienes pero no es mas que una función que concatena valores en un string al estilo del Concat de MySql. Lo que pasa es que a su vez llama a otras funciones ...... te paso todo lo que hace falta

//Devuelve la suma de n datos de cualquier tipo como un string
Function Concat(aValues,cSeparator,lSql)
Local nItem:=0, cValue:="", cResult:=""

Default lSql to .F.

If cSeparator=Nil
cSeparator:=If(!lSql," ",",")
Endif

For nItem:=1 to Len(aValues)
If lSql
IF ValType(aValues[nItem])="C" .And. ( Left(aValues[nItem],12)=="{\rtf1\ansi\" .Or. Left(aValues[nItem],14)=="{\\rtf1\\ansi\" )
aValues[nItem]:=XA_RtfToTxt(aValues[nItem])
Endif
cValue:=ValToSql(aValues[nItem])
Else
cValue:=AllString(aValues[nItem])
Endif

cResult:=cResult+cValue+cSeparator

Next

Return Left(cResult,Len(cResult)-Len(cSeparator))


//*/
// FUNCION que comprueba cadenas a enviar a sentencias Sql para ver si tienen apóstrofo o comillas y que no fallen

// @author Bingen Ugaldebere
// @param xSql Valor de cualquier tipo para montar la sentencia Sql, si es un DateTime puede ser un char '2015-12-01 12:30:00' o un array {Fecha,hora}

// @return Cadena montada para su uso en sentencias Sql
///

FUNCTION ValToSql( xSql )
Local cSql:=""


DO CASE
CASE ValType( xSql ) = "U" .Or. xSql=Nil
cSql := "Null"
CASE ValType( xSql ) = "C" .OR. ValType( xSql ) = "M"
cSql := "'" + StrSql( xSql ) + "'"
CASE ValType( xSql ) = "T" //TimeStamp con milisegundos
cSql := iif( Empty( xSql ), "'0000-00-00 00:00:00'", "'"+SqlDatetime(CToD(Left(ToString(xSql),10)),SubStr(ToString(xSql),12,8))+"'" )
CASE ValType( xSql ) = "DT" .Or. ( ValType( xSql ) = "A" .And. EsFecha(xSql[1]) .And. EsHora(xSql[2]) )
cSql := iif( Empty( xSql ), "'0000-00-00 00:00:00'", "'"+SqlDatetime( xSql[1], xSql[2] )+"'" )
CASE ValType( xSql ) = "D"
cSql := iif( Empty( xSql ), "'0000-00-00'", "'"+DToSQL( xSql )+"'" )
CASE ValType( xSql ) = "L"
cSql := IF( xSql, "1", "0" )
OTHERWISE
cSql := StrSQL(AllString( xSql ))
END

RETURN cSql

**/
* FUNCION que Convierte una fecha y hora a un DATETIME de MySql YYYY-MM-DD HH:MM:SS

* @author Bingen Ugaldebere
* @param dDate Fecha de referencia

* @return cadena con el 'YYYY-MM-DD HH:MM:SS'
*/

Function SqlDatetime(dDate,cTime)
Local cDate
Default dDate to Date()
Default cTime to Time()

cDate:=Dtoc(dDate)
If Len(cTime)=5 //Por si la hora viene sin segundos
cTime+=":00"
Endif
Return IF(Vacio(dDate),"0000-00-00",Right(cDate,4)+"-"+Substr(cDate,4,2)+"-"+Left(cDate,2))+" "+IF(Vacio(cTime),"00:00:00",cTime)


**/
* FUNCION Indica si un dato esta vacio de verdad con .T. o .F.

* @author Bingen Ugaldebere

* @param xValue Valor a considerar si está vacio de cualquier tipo
* @param xReturnValue [Opcional] Si no se indica este valor se devuelve simpre .T. / .F.
* pero si se indica se devuelve xReturnValue si está vacio pero se devuelve xValue si no lo está
* @param lNil [Opcional] Si se indica .T. también los campos de texto que sean "Nil" se considerarán como vacios, con .F. no se tienen en cuenta

* @return .T. / .F. o xReturnValue (si esta vacio y se ha suministrado)
*/

Function Vacio(xValue, xReturnValue, lNil)
Local lVacio:=.F.

Default xReturnValue To .T. //Valor de retorno por defecto If Vacio( cCiudad, "Bilbao")
Default lNil To .F. //EN caso de ser un tipo "C" con valor "NIL" y lNil:=.T devuelve la cadena "NIL"

Do CAse
Case xValue=Nil
lVacio:=.T.
Case ValType(xValue)="D" //Date
lVacio:=xValue=Ctod(" - - ")
Case ValType(xValue)="T" //DateTime
lVacio:=ToString(xValue)==" - - 00:00:00.000"
Case ValType(xValue)="A" //Array
lVacio:=Len(xValue)=0
Case ValType(xValue)="L" //Boolean
lVacio:=xValue=.F.
Case ValType(xValue)="N" //Numeric
lVacio:=xValue=0
Case ValType(xValue)="B" //Codeblock
lVacio:= ToString(xValue) == "{|| ... }"
Case ValType(xValue)="C" .And. lNil .And. Upper(xValue)=="NIL" //Char
lVacio:=.T.
CASE ValType(xValue)="H" //Hash
lVacio:= Empty( xValue )
OtherWise
lVacio:=Len(AllString(xValue))=0
EndCAse

Return IF(lVacio,xReturnValue,IF(ValType(xReturnValue)<>"L",xValue,.F.))



**/
* FUNCION que Devuelve .T. si una variable suministrada en realidad es una fecha

* @author Bingen Ugaldebere
* @param xFecha Fecha en formato Date o Character

* @return .T. / .F.
*/

Function EsFecha( xFecha )
Return Alltrim(ToString(xFecha)) == Dtoc(CToD(Alltrim(ToString(xFecha))))

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// FUNCION : ValHora(cHora) -> lCorrecto
// DESCRIPCION: Valida una cadena como expresión horaria del tipo HH:MM:SS
//
// PARAMETROS : <cHora> = Cadena con formato HH:MM:SS que representa una expresión horaria.
// El separador de horas y minutos puede ser cualquiera, pero tiene que existir.
//
// REGRESA : <lCorrecto> - Valor lógico que indica si la cadena <cHora> es una expresión horaria
// con el formato HH:MM:SS
//
// EJEMPLO : cHora := SPACE(8)
// @ 0, 0 SAY "Indique hora: " GET cHora PICTURE "99:99:99" VALID ValHora(cHora)
//
// -> La función ValHora() no dejará continuar el prgorama hasta que la expresión
// horaria sea correcta.
//
// A U T O R : Grupo Eidos (Librerias en Clipper 5, Ra-ma, Addison-Wesley Iberoamericana)
// F E C H A : 1992
//
FUNCTION ValHora( cHora )

// Control de errores
IF ValType( cHora ) != 'C'
RETURN .F.
END IF

cHora := AllTrim( cHora )
IF Len( cHora ) < 8
cHora := Left( cHora + "00:00:00", 8 )
ENDIF

RETURN Val( Left ( cHora, 2 ) ) <= 23 .AND. ;
Val( Left ( cHora, 2 ) ) >= 0 .AND. ;
Val( SubStr ( cHora, 4, 2 ) ) <= 59 .AND. ;
Val( SubStr ( cHora, 4, 2 ) ) >= 0 .AND. ;
Val( Right( cHora, 2 ) ) <= 59 .AND. ;
Val( Right( cHora, 2 ) ) >= 0

FUNCTION EsHora( cHora )
Return ValHora( cHora )
/
BiSoft Desarrollo de software profesional
http://www.bisoft.es
Avatar de Usuario
gerencia
Mensajes: 282
Registrado: Sab Jun 20, 2009 6:01 pm

Re: Sincronizar tablas Maridb

Mensaje por gerencia »

Como siempre Bingen muy oportuno.

Mil gracias,
Jose David Angulo Urzola
Auditoria y Sistemas Ltda.
Cartagena-Colombia
Avatar de Usuario
ignacio
Site Admin
Mensajes: 9243
Registrado: Lun Abr 06, 2015 8:00 pm
Ubicación: Madrid, Spain
Contactar:

Re: Sincronizar tablas Maridb

Mensaje por ignacio »

Un ejemplo utilizando DBFs locales con DBF es un servidor Web y una clase que hace todo el trabajo de sincronización:

Código: Seleccionar todo

METHOD DbfPostAppend( oSender ) CLASS TMainForm

   LOCAL oRec := oSender:GetRecord()

   IF !::oDbfSyncro:Send( 0, Lower(oSender:cAlias), oRec:ToJson(.t.) )
      MsgAlert( "Los últimos cambios realizados no pudieron sincronizarse" + ;
               CRLF + CRLF + ::oDbfSyncro:cLastError )
   ENDIF

RETURN Nil

METHOD DbfPostSave( oSender ) CLASS TMainForm

   LOCAL oRec := oSender:GetRecord()

   IF oSender:lOnAppend() // Ya se ha grabado
      RETURN NIL
   ENDIF

   IF !::oDbfSyncro:Send( oSender:Recno(), Lower(oSender:cAlias), oRec:ToJson(.t.) )
      MsgAlert( "Los últimos cambios realizados no pudieron sincronizarse" + ;
               CRLF + CRLF + ::oDbfSyncro:cLastError )
   ENDIF

RETURN Nil

METHOD DbfCliPreDelete( oSender ) CLASS TMainForm

   IF !::oDbfSyncro:Send( -oSender:Recno(), Lower(oSender:cAlias) ) // Se pasa el número de registro en negativo
      MsgAlert( "Los últimos cambios realizados no pudieron sincronizarse" + ;
               CRLF + CRLF + ::oDbfSyncro:cLastError )
   ENDIF

RETURN Nil
Ignacio Ortiz de Zúñiga
[Equipo de Xailer / Xailer team]
https://www.xailer.com
Avatar de Usuario
gerencia
Mensajes: 282
Registrado: Sab Jun 20, 2009 6:01 pm

Re: Sincronizar tablas Maridb

Mensaje por gerencia »

Ignacio,

muy buena opción,

Muy agradecido.
Jose David Angulo Urzola
Auditoria y Sistemas Ltda.
Cartagena-Colombia
Responder