Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   MySQL (https://www.clubdelphi.com/foros/forumdisplay.php?f=21)
-   -   Que tan confiable es MySQL (https://www.clubdelphi.com/foros/showthread.php?t=90441)

darkerbyte 09-06-2016 05:30:15

Que tan confiable es MySQL
 
Saludos estimados colegas.

Estoy muy agradecido con todos los compañeros que hacen posible este foro, la verdad he aprendido muchísimo de ustedes.

Pues estoy atorado en un problema. Tengo un sistema de Punto de Venta, en el cual mi cliente ha tenido serios problemas ya que tenemos diferencias de existencias en sus inventarios.
Ya me volví loco haciendo cientos de pruebas de escritorio, sin embargo no he logrado que el programa me genere un solo error y al cliente en cambio, le generan múltiples errores.

Ya he revisado mi código, trazado los procesos una y otra vez, revisado las consultas que se mandan al servidor, etc.

Y me estaba preguntando si mi problema será quizá que no estoy usando la versión oficial de MySQL sino la que viene modificada en WAMP (http://www.wampserver.com/en/)
O quizá los controles del Delphi. Aquí pongo lo que estoy usando

WAMP 2.0 (MySQL 5.1.33, Motor InnoDB)
Delphi XE5
Zeos 7.1.4-stable

Disculpen si mi pregunta es hasta cierto punto ingenua, solo quisiera con la experiencia de ustedes descartar cualquier posibilidad.


El código que envío para registrar una venta es así, tengo una tabla de productos con un campo llamado 'Existencia', el sistema
conforme vende tiene que ir descontando. Pero hay veces en las que no hace el descuento de las piezas vendidas.

Código SQL [-]
START TRANSACTION;
INSERT INTO ventas(idVenta, fecha, hora, cliente, vendedor, turno, descuento)
                 VALUES (16, '2016-06-08', '22:19:12', 1, 2, 1, 0.00);
INSERT INTO ventas_partidas(idVenta, idProd, cantidad, precio_compra, precio_venta)
                  VALUES(16, 'PDE', 3.00, 29.00, 35.00);
UPDATE productos SET almacen=almacen-3.00 WHERE clave='PDE';   -- Esta instrucción es donde esta mi duda

Esto lo hago en un try, si se ejecuta correctamente envío una consulta con un "commit", de lo contrario envío "rollback"

Casimiro Notevi 09-06-2016 08:52:20

Efectivamente, si el cliente tiene problemas es por algún error en tu software, no lo dudes.
Debes ir físicamente al negocio de tu cliente, ver cómo trabajan, si hacen algo diferente a como lo haces tú en tus pruebas. Probar con su base de datos, etc.

newtron 09-06-2016 09:26:30

¿Solamente haces ese proceso de inserción? ¿No hay modificaciones ni anulaciones?

Por otro lado, y por experiencia, te puedo decir que lo más sano es que los procesos para actualizar distintos campos a partir de inserciones, modificaciones o anulaciones en tablas de movimientos estén en triggers de la base de datos. De esa manera tendrás bastantes menos problemas.

Saludos

fjcg02 09-06-2016 09:31:00

Cita:

Empezado por newtron (Mensaje 506015)
Por otro lado, y por experiencia, te puedo decir que lo más sano es que los procesos para actualizar distintos campos a partir de inserciones, modificaciones o anulaciones en tablas de movimientos estén en triggers de la base de datos. De esa manera tendrás bastantes menos problemas.

^\||/

Saludos

bitbow 09-06-2016 18:11:07

Solo por molestar un poco, de casualidad tu cliente maneja multiples puntos de venta (varios equipos con su soft atacando la misma base de datos), como gestionas esto?

Veo que en tus inserts no manejas un identificador para el punto de venta o terminal.

Saludos.

mamcx 09-06-2016 18:55:43

Otra buena practica es usar logs. En cada paso importante pones algo como

Cita:

Log.debug("Inv.Add", "id=16, date='2016-06-08', date='22:19:12', total=1"),
Y luego asi puedes evaluar posteriormente el flujo del proceso.

AUN mas confiable si haces el LOG *dentro* de la BD. Creas una tabla LOG y haces similar.

Si haces un Log *correcto*, deberias poder reconstruir con 100% de fidelidad los datos derivando SOLO del log.

darkerbyte 09-06-2016 19:22:59

Gracias Casimiro, si efectivamente estoy haciendo las pruebas que me comentas. Solo quería descartar cualquier otra posibilidad. Gracias por tu respuesta

darkerbyte 09-06-2016 19:26:46

Triggers
 
Cita:

Empezado por newtron (Mensaje 506015)
¿Solamente haces ese proceso de inserción? ¿No hay modificaciones ni anulaciones?

Por otro lado, y por experiencia, te puedo decir que lo más sano es que los procesos para actualizar distintos campos a partir de inserciones, modificaciones o anulaciones en tablas de movimientos estén en triggers de la base de datos. De esa manera tendrás bastantes menos problemas.

Saludos

Hola Newtron.

Aprecio mucho tu tiempo para responder y por tu consejo.

Basicamente son 3 tablas. Una con el inventario de productos, una para registro de ventas y otra con el detalle de la venta. La situación es que solo hago inserts cuando se hace una venta.
Cuando se hace una devolución de algun producto o la cancelación de la venta solo marco el ID de la cancelación el que guardo en una tabla de cancelaciones y reintegro el producto al inventario.
No se si para estos casos es posible programar un trigger ya que se haría una modificación solo en una columna de la tabla. No soy muy experto en el tema de los triggers.

darkerbyte 09-06-2016 19:35:04

Cita:

Empezado por bitbow (Mensaje 506030)
Solo por molestar un poco, de casualidad tu cliente maneja multiples puntos de venta (varios equipos con su soft atacando la misma base de datos), como gestionas esto?

Veo que en tus inserts no manejas un identificador para el punto de venta o terminal.

Saludos.

Si maneja multiples terminales, incluso manejamos sucursales vía internet.

No puse todos los campos de la tabla en la consulta que escribí arriba a fin simplificar mi ejemplo. La tabla de Ventas tiene como llave primaria (idSucursal, idVenta) ambas de tipo entero con la que identifico en que sucursal se realizó la venta. Claro, esta misma llave esta en la tabla ventas_det la cual tiene como llave primaria (idSucursal, idVenta, idProducto) siendo el ultimo de tipo string que es una llave foranea a la tabla de productos. La tabla de Ventas también tiene una columna llamada "idVendedor" con la que identifico que vendedor hizo la venta. No llevo un control por máquina ya que los vendedores pueden abrir multiples instancias del programa en diferentes computadoras usando su mismo id de usuario y estas ventas al hacerse el corte se filtran en base al vendedor que las realizó.

Para registrar la venta lo que hago es obtener el primero el siguiente folio y después genero la consulta (como la que puse arriba, mas menos productos).
Si ocurre un error (que se intentaran registrar dos ventas en el mismo instante y por ende el folio de la venta que llegó posterior está repetido) el sistema manda un rollback de la transaccion que fue rechazada, actualizamos el folio e intentamos registrarla nuevamente.

Estoy haciendo algo mal? quiza me pueden sugerir una mejor manera de hacer las cosas.

De verdad muchas gracias por sus comentarios

darkerbyte 09-06-2016 19:37:21

Gracias mamcx

Tienes razón, voy a programar el log porque es un dolor de cabeza esto.

bitbow 10-06-2016 00:19:32

Ya que nos ampliaste la info sobre le problema lo único que queda son los logs y como indican los compañeros en sus comentarios, si el fallo lo tiene el cliente y tu no puedes replicarlo vuela al lugar del cliente (después de añadir logs por donde se te ocurra) y espera a ver el error.

Saludos.

Al González 10-06-2016 05:08:34

Cita:

Empezado por darkerbyte (Mensaje 506010)
[...]tengo una tabla de productos con un campo llamado 'Existencia'
Código SQL [-]
START TRANSACTION;
INSERT INTO ventas(idVenta, fecha, hora, cliente, vendedor, turno, descuento)
                 VALUES (16, '2016-06-08', '22:19:12', 1, 2, 1, 0.00);
INSERT INTO ventas_partidas(idVenta, idProd, cantidad, precio_compra, precio_venta)
                  VALUES(16, 'PDE', 3.00, 29.00, 35.00);
UPDATE productos SET almacen=almacen-3.00 WHERE clave='PDE';   -- Esta instrucción es donde esta mi duda

1. ¿El campo se llama realmente Existencia o almacen?

2. Te recomiendo prescindir de valores literales en sentencias SQL de aplicaciones que no son de uso interno o de pruebas. Es mejor y más seguro usar parámetros ("...Values (..., :Cantidad..."). Quizá en la computadora del cliente 3.00 no sea lo que tú crees que es (configuración regional).

3. ¿Existe la remota posibilidad de que tengas dos productos con la misma clave?

4. ¿Tendrás algún disparador (trigger) involucrado con esas tablas y que no estés observando?

5. ¿Las diferencias inesperadas que se presentan son consistentes o varían sin sentido? ¿El error se presenta bajo un patrón común respecto a las cantidades que debería haber y las que hay?

Saludos misteriosos.

darkerbyte 10-06-2016 17:07:45

En revisión
 
Gracias Al González.

Siempre es bueno repasar lo básico, no es la primera vez que me pasa estar haciendo algo mal. Respecto a lo que me comentas

1. ¿El campo se llama realmente Existencia o almacen?
El campo se llama almancén, y lo uso para llevar la existencia. Me equivoqué al formular la pregunta.

2. Te recomiendo prescindir de valores literales en sentencias SQL de aplicaciones que no son de uso interno o de pruebas. Es mejor y más seguro usar parámetros ("...Values (..., :Cantidad..."). Quizá en la computadora del cliente 3.00 no sea lo que tú crees que es (configuración regional).
Aprecio el consejo voy a hacer los cambios para que sea por parámetros

3. ¿Existe la remota posibilidad de que tengas dos productos con la misma clave?
No, en la tabla de Productos la clave es llave primaria y el sistema hace una revisión antes de modificar o insertar nuevo producto.

4. ¿Tendrás algún disparador (trigger) involucrado con esas tablas y que no estés observando?
Gracias ya revisé. No tengo de momento triggers en esta parte.

5. ¿Las diferencias inesperadas que se presentan son consistentes o varían sin sentido? ¿El error se presenta bajo un patrón común respecto a las cantidades que debería haber y las que hay?
Varían sin sentido.


Por eso mi pregunta sobre la confiabilidad de MySQL. De hecho otro cliente tenía un problema parecido. A este cliente cuando hacía sus ventas en sus tickets (Impresión en RAW) y notas (hechas con QuickReport) los precios salían distintos a los que tenemos guardados en el sistema. Me volví loco haciendo pruebas, depurando una y otra vez las rutinas del programa. Ya desesperados le dimos formato a sus computadoras y problema solucionado. Aun sigo sin poder explicarme que fue lo que estaba pasando.

De momento estoy haciendo lo del Log que me han sugerido, estoy también haciendo revisión en las rutinas. Por último mi tirada es hacer pruebas para cambiar el servidor del 5.1 a 5.7.
Bueno, les mantendré informados que pasa. Gracias a todos por su tiempo y sus sugerencias

bitbow 10-06-2016 17:12:28

La realidad es que en mi experiencia personal MySql aunque recomendable para sistemas web, en sistemas de escritorio corre lento por lo que he optado por firebird y firebird llegando a usarlo solo cuando el cliente así lo requiere, además de que me he topado con que hay errores que no gestiona adecuadamente como que este mal la sintaxis y aún así compile los procedimientos (igual y es la versión), es verdad que es una base robusta con muchas funcionalidades en cuanto a seguridad, librerías, tipos de datos, etc pero prefiero firebird.

mamcx 10-06-2016 17:47:28

Cita:

Empezado por darkerbyte (Mensaje 506076)
Varían sin sentido.

Podrias dar un ejemplo de como varian? Como era antes (bueno) como es despues(malo) y como deberia haber sido?

darkerbyte 10-06-2016 19:55:24

Mamcx

El cliente me reporta que hacen una venta y los productos que ha vendido los cuales deberían haber sido descontados siguen apareciendo en existencia.
Por eso comenté que tenía mi duda al respecto con esta instrucción:

UPDATE productos SET almacen=almacen-3.00 WHERE clave='PDE'; --En este caso se vendieron 3 pzas del producto 'PDE'

Para cada producto que se vende se inserta la partida en la tabla de "ventas_det" y se hace el desconteo respectivo en la tabla de productos. Después de revisar el kardex veo que efectivamente tengo existencia de más del producto.
Esto es muy extraño porque el algoritmo genera las dos consultas, tanto para insertar como para descontar y lo hace dentro de una transacción y he probado cientos de veces, sin exagerar quizá miles de veces el procedimiento, tranzando paso a paso.
Igual al cliente no se lo hace en todas las ventas, ni en todos los productos.
He cuidado que las claves de los productos de los clientes no tengan símbolos especiales (comillas, asteriscos, etc). Para que no pudieran dar alguna falla en la consulta por el propio lenguaje de MySQL

Creo que es cosa del Chamuco, como decimos por acá.

Casimiro Notevi 10-06-2016 20:06:12

Cita:

Empezado por darkerbyte (Mensaje 506084)
UPDATE productos SET almacen=almacen-3.00 WHERE clave='PDE'; --En este caso se vendieron 3 pzas del producto 'PDE'

Eso no son 3 piezas, sino 3.00 :)

mamcx 10-06-2016 20:10:29

Conceptualmente es el problema que resuelve la contabilidad doble: Si no se conserva la historia y flujo de las transacciones se pueden generar errores (o fraudes).

El problema de fondo es que el manejo del estado de los datos normales en forma CRUD es *destructivo* y no hay manera de saber como se llego al resultado actual. Para un inventario, al igual que un libro contable - de hecho, un inventario es una forma especial de contabilidad- es mas solido usar un diseño inmutable (donde no se sobreescribe sino que se agregan los resultados).

Osea, un log.

Es mejor que el inventario sea asi:

Cita:

Feb1 - Ref:ABC BUY = Qty:1, Value:$10
Feb2 - Ref:ABC SELL = Qty:1, Value:$15
Feb3 - Ref:ABC BUY = Qty:10, Value:$20
Feb3 - Ref:ABC TRANSFER = Qty:10, Value:$20 TO Store: 2
Nota: Se registra luego de hacer las validaciones. Por eso, si no se permiten vender por debajo del stock, no saldran lineas con inventarios negativos. Osea, se registra solo un hecho real y concreto (en el pasado) y no lo que se va a hacer y que *puede* ser rechazado por la logica del negocio.


Y usar la tabla de inventarios como "cache" con el valor actual del stock (Asi que obvio tienes un campo Stock actual). Asi, teniendo la historia en forma de log, puedes tener claro como se movio el flujo del inventario.

Entonces, si hay un problema con un producto, puedes consultar tu log y ver en que momento se genero el problema.

darkerbyte 17-06-2016 17:02:03

Avance
 
Ya hice la complementación para generar un archivo log. Para cada procedimiento el programa guarda lo que el usuario tiene en pantalla y las consultas que el sistema envía al servidor. Ahora a dar un poco de tiempo para empezar a rastrear los archivos

darkerbyte 08-10-2016 19:26:54

Conclusión
 
Usando la idea de Mamcx agregué a cada procedimiento que "mete mano" a la base de datos un log de la información que tengo en pantalla junto con la consulta que está enviando a MySQL para serciorarme que no fuese un error de programación mío.

También implementé en el sistema un método para capturar el estado del inventario (screenshot) que almacena para cada producto
la existencia en cada sucursal junto con el costo del producto. Por supuesto se guarda también la fecha y hora de captura.

Mis clientes volvieron a tener problemas con las existencias de su inventario.
El día 4 estuve haciendo unas pruebas, hice una captura de inventario, el día 5 se registró un movimiento para dos productos
fue una única venta, en el archivo log esto es lo que tenía en pantalla:



Cantidad Clave Descripción Precio Unitario, importe

0.50 ; 520100106; ULTRAFONDO TRANSP. CATALIZABLE GRANEL; Menudeo; 85.50; 42.75 ;
0.50 ; 521101806; CATALIZADOR AL 100% P/ULTRAFONDO GRANEL; Menudeo; 84.50; 42.25 ;


Y aquí lo que se mandó al servidor
Código SQL [-]
START TRANSACTION;
INSERT INTO ventas VALUES (1,8126, '2016-10-05', '17:42:48', 1, 3,282,'',0, 0, 0, 0);
INSERT INTO pedidos VALUES(1,8126,'520100106',0,0.50,1,65.96,85.50);
INSERT INTO pedidos VALUES(1,8126,'521101806',1,0.50,1,65.01,84.50);
UPDATE productos set suc1=suc1-0.50 WHERE clave='520100106';
UPDATE productos set suc1=suc1-0.50 WHERE clave='521101806';
Commit;

El punto es este, pueden ver que en las dos ultimas lineas se resta de la existencia en la sucursal 1 [suc1(decimal(8,2)] la cantidad vendida 0.5
En el screenshot que hice de mi inventario tenía 9.70 piezas y después de esta venta tengo una existencia de 6.20 en el producto "520100106, ULTRAFONDO TRANSP. CATALIZABLE GRANEL"

Escribo el resultado de mis pruebas por si alguien más a futuro pasa por una situación similar. Versión de MySQL la 5.1.33


Voy a probar con otra versión de MySQL porque ya me volví loco haciendo debugs y probando cientos de veces el codigo fuente
o quizá me cambie a Firebird


La franja horaria es GMT +2. Ahora son las 10:41:12.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi