Visual Foxpro + MySQL

Siempre trabaje con Visual FoxPro y DBF Libres, a la hora de grabar sobre mis tablas solo hacia un go bottom y obtenía el ultimo registro +1 y grababa la información.
Después cree una tabla de parámetros para lo que era la facturación (Facturas, Boletas, Guías) bloqueaba el registro correspondiente le aumentaba el valor en 1 y desbloqueba con (RLOCK() y UNLOCK).
Nunca me preocupe por el tema de las transacciones y si es que no se completaba la operación correspondiente, por que casi no tuve problemas.
Ahora estoy comenzando a desarrollar en Visual Foxpro +MySQL.
Y lo primero que veo es que para traer la información de MySQL a Visual Foxpro tengo que crear una conexión, y luego de verificar que es exitosa, utilizar el SQLEXEC, para traer los datos almacenado en un cursor, hasta ahí todo bien, pero el problema es como hago para grabar esa información, hasta donde entiendo es así:
SQLEXEC(nCon,"Select * from Cliente","Tabla")
SELECT Tabla
CURSORSETPROP( [Buffering], 5 )
APPEND BLANK
...
...
SELECT MainTabla
Tableupdate(.t.)
CURSORSETPROP( [Buffering], 1 )
SQLEXEC(nCon,"Insert into Cliente (codcli, nomcli) values ("1","UNO")
Claro esta que los valores a agregar a la Tabla del MySQL los obtengo del Cursor "Tabla"
Pero lo que quiero es un ejemplo de como hacer una Factura con Cabecera y Detalle y que los datos se almacenen en las Tablas del MySQL y si hay un problema de cancele todo, porque mi confusión parte de que tendría primero que controlar que los datos se graben bien en el cursor de Cabecera de Factura y en el Cursor de Detalle de Factura, para luego controlar que estos datos que se han almacenado en los cursores pasen a la es Tablas del MySQL, estoy en lo correcto o es que me estoy confundiendo en este tema.
Respuesta
1
Vamos por parte, lo que ya estas aprendiendo de mysql y fox esta bien encaminado, te daré unos tips para que veas algunos consejos. Primero en mysql no existe go bottom seek locate etc, solo utilizaremos comandos sql puros, propondremos las dos tablas siguientes como ejemplo.
Faccab. Cabecera y facitems detalle de factura.
faccab       fac_cod  , fac_fecha,  fac_cliente, fac_total      
facitems      item_cod,   item_producto, item_cant, item_precio
Bueno, hay varias maneras de obtener el valor máximo de la factura antes de que grabes, por ej. así. Obtenemos el mayor valor actual del numero de factura de faccab y lo retornamos.
=sqlexec(xCon,"select max(fac_cod) as numeroFactura from faccab group by fac_cod","cursor")
Return(cursor.numeroFactura)
Pero la forma ideal es utilizar los campos autonuméricos, o sea cuando creas la tabla le defines como llave primaria (PORQUE) y la opción AUTOINCREMENT lo que hará el mysql es enumerarlo automáticamente cuando hagas la inserción, luego cuando insertas la cabecera obtenés el numero ultimo y eso lo guardas en el detalle, así.
Antes de comenzar con el ejemplo te hablo sobre las transacciones, las transacciones son posibles en mysql a partir de las versiones 4.x, pero para ello tus tablas tienen que ser del tipo INNODB, por que las tablas myIsam no soportan transacciones. Ok, aquí va
=sqlexec(xCon,"BEGIN")            &&abro una transaccion
if sqlexec(xCon,"insert into faccab set fac_fecha = '"+dtoc(variableFecha)+"', fac_clie="+str(xCodCLiente)+", fac_total = "+str(xTotal))>0
           =sqlexec(xControl,"SELECT LAST_INSERT_ID();","xDepCod")>0
           sele xDepCOD
           xDepCod = field(1)
           xDepCod = &xDepCod
           if sqlexec(xCon,"insert into facitems set item_cod="+str(xDepCod)+", item_proc='"+xCodigoProducto+"', item_cant="+str(xCantidad)+", item_precio="+str(xPrecio))>0
                 =sqlexec(xCon,"COMMIT")
                messagebox("Guardado satisfactoriamente")
          else
                   messagebox("Error al grabar el item, deshaciendo transaccion ")
                 =sqlexec(xCON,"Rollback")
          endif
else
       Messagebox("Error al grabar la factura")
Endif
Como puedes ver aquí use la función last_insert para obtener de mysql cual fue el ultimo registro insertado, a diferencia del primer ejemplo que te di, luego el ejemplo es muy simple, no inicialice las variables que supongo entenderás porque, no es muy cómodo escribir en este editbox, bueno, si tienes dudas aun me avisas
En serio eres un verdadero amigo al tomarte la molestia de responder mi mensaje, y más aun de la forma como lo hiciste, me has despejado varia dudas, pero tengo unas preguntas adicionales:
1. El autoincremental va ha ser desde 1 y así ... 2,3, o puede ser que yo le de un formato especifico como empezar en 500001 y de ahí para adelante.
2. La Parte de la Transacción me quedo claro y como es el manejo de los datos, pero tengo una duda sobre los datos de la cabecera y el detalle, yo creo dos cursores obtenidos de selects a las tablas (CABECERA y DETALLE DEL MYSQL) con la estructura de la cabecera y el detalle luego almaceno los datos en ambos cursores con las funciones (CURSORSETPROP, TABLEUPDATE, TABLEREVERT) una vez que ya han sido almacenadas en los cursores recién hago lo que tu me indicas en tu ejemplo. Esta bien lo que estoy haciendo o esta mal. O como debo hacer.
3. Para tablas que no involucran cabecera y detalle, y son solo tablas padres como Productos, Clientes, hago lo que te indique en mi ejemplo del primer mensaje. O también adicionalmente debo agregarle el código de las transacciones para asegurar el grabado.
4. Y que me puedes hablar de que dos usuarios estén manejando el mismo registro y se presente un conflicto.
Gracias,
Saludos,
Sandro
Sin problemas, con respecto a tus preguntas.
1- Si, el autoincremental comienza por defecto con 1, pero de repente al primer registro le insertas 5000 el siguiente sera automáticamente 5001, el único requisito es que no repitas un mismo valor. Ej, si insertas en forma automática 1,2,3, luego tu le fuerzas al valor 10, la próxima el automáticamente se pone 11.
2- No entendí bien esta pregunta, yo me imaginaba que tienes un programa de facturación, creaste un cursor temporal con CREATE CURSOR TEMP(CÓDIGO c(10), DESCRIP c(30), CANT (12,2)), luego capturas los datos en unos textbox y cuando el usuario quiera guardarlos tomas el temp, lo recorres y lo guardas así como el ejemplo, lo que no entendí es que traes de mysql los datos de cabecera e items de mysql, ¿o sea ya esta guardado la factura?, ¿Y lo vuelves a guardar?, si me aclaras este punto tal vez te ayude mejor, pero también hay que tener en cuenta que tableupdate() y tablerevert() no afecta al mysql a no ser que trabajes con vistas remotas, si no estas trabajando con vistas remotas no necesitas de estas funciones, el método que yo te di como ejemplo se llama paso SQL, o sea le pasamos al motor mysql unos comandos y el los procesa localmente, el método de vistas remotas es totalmente diferente, creo que se esta mezclando las cosas, yo particularmente no trabajo con vistas remotas, por que no es muy eficiente cuando comenzamos a hablar de tablas con más 500 mil registros.
3- La transacción es totalmente opcional, si no ejecutas BEGIN el motor mysql no tiene problemas, los guarda sin pedir COMMIT, para tablas como productos, clientes etc no es necesario crear transacciones, debido a que la integridad referencial no es afectado. Pero si quieres puedes usarlo, no te dará ningún error,.
4- EN MYSQL no existe el problema de conflictos, por que los bloqueos se realizan en forma automática, pero también puedes bloquear las tablas para lectura, escritura etc, generalmente para cuando vayas a ejecutar algún query muy largo, con esto aseguras que nadie lo toque, o cuando vas a guardar algún dato ej.
LOCK TABLES clientes WRITE;
INSERT into clientes set.......
El ejemplo anterior bloqueamos la tabla clientes para escritura, y luego abajo comenzamos a cargar datos, es un ejemplo, pero conflictos no tendrás, ahora si estas trabajando con vistas remotas, mudate al método paso sql, por que lo único que manejas son tres funciones básicas.
Sqlconnec()
Sqlexec()
Sqldisconnec()
Y nada más, luego es puro fox con cursores de lectura/escritura, espero haberte ayudado, y suerte en tu emprendimiento.
Gracias por tus respuestas, con respecto a la 2 pregunta:
Por Ejemplo si es una tabla como clientes, yo hago un sqlexec(ncon,"select * from clientes"), AFIELDS (arreg) ,create cursor (pcursor1) from array arreg ,select cursor,ZAP, APPEN FROM DBF (SQLResult) y asi  traigo todos los datos, los cargo en una grilla  y a los objetos de mi formulario en su controlsource les pongo el nombre del cursor con su respectivo campo y luego a la hora de grabar (ya sea ingreso,modificacion o eliminacion) hago uso del (CURSORSETPROP,TABLEUPDATE,TABLEREVERT), segun sea el caso y luego hago el insert,update o delete en la tabla del mysql con el dato que se este trabajando en el cursor.
Para lo de las facturas es igual solo que que pongo sqlexec(ncon,"select * from cabfac where 1=2"),AFIELDS(arreg),USE,create cursor (pcursor1) from array arreg, en ambos casos el nombre del cursor pcursor1 es un parametro que toma el mismo nombre de la tabla.
En la tabla de Clientes, Productos si traigo todos los Datos, solo en Factura, Detalle de Factura solo traigo la estructura, no trabajo con vistas.
Cabe señalar que tengo un procedimiento que lee automáticamente los campos y datos del registro que se esta trabajando del cursor y lo almacena en las tablas de mysql según sea el caso (INSERT, UPDATE, DELETE)
O Como haces para hacer por ejemplo un mantenimiento de una tabla, para poder guiarme mejor.
Saludos
Sandro
Disculpa por la tardanza, es que los sábados tengo viajes y generalmente no respondo, bueno, con respecto a la forma en que trabajas creo que no es necesario todo el proceso que acabas de mencionar, lo de crear un cursor a partir de un array, ya que
=sqlexec(xCon,"select * from clientes","clientes")
Ya te crea un cursor de lectura/escritura totalmente utilizable, este ejemplo te creara un cursor llamado clientes, el mismo nombre de tu tabla mysql, bueno, con respecto a tu duda sobre un mantenimiento yo prefiero generalmente con un form que tenga un list o grid de consulta o búsqueda, al iniciar lo cargo con los datos actuales de la tabla, pero solo las columnas necesarios, por ej select cli_codigo, cli_nombre from clientes, luego cuando es seleccionado el valor en el grid lo despliego en los objetos correspondientes, los busco generalmente con un sql así
=sqlexec(xCon,"select * from clientes where cli_codigo  = "+str(thisform.text1.value),"temp"
Luego tomo temp y pongo cada field en los objetos, cuando se va almacenar un dato es un simple insert, o delete según el caso, los update hago también así
=sqlexec(xCon,"update clientes set cli_nombre = "+alltr(thisform.text2.value)+" where cli_codigo ="+str(thisform.text1.value)  
no es recomendable traer todos los datos de las tablas por que cuando vayan creciendo información esto sera lento y consumira muchos recursos del servidor, es importante traer solo lo necesario, no esta mal como trabjas, tal vez con el tiempo necesites optimizar algunas cosas para lograr mayor velocidad, prueba con tablas de 10.000 registros, si no es lento, entonces puedes continuar.

1 respuesta más de otro experto

Respuesta
1
Cuando los datos se encuentran en la aplicación, es decir, ya los extrajiste de MySQL se comportan de la misma manera que cualquier otra tabla.
Esta bien pero esperaba más, por ejemplo como se comportan los datos en los cursores cuando hay más de uno usuario usando la tabla y como es que cuando estos datos son enviados al servidor controlarlos con TRANSACTION, ROOLBACK, COMMIT, por si no se puede completar la operación.
Saludos
Sandro

Añade tu respuesta

Haz clic para o

Más respuestas relacionadas