Necesito sus opiniones sobre el diseño de una tabla de stock

2,837 views
Skip to first unread message

Daniel Del Giudice

unread,
Jun 10, 2012, 8:46:55 PM6/10/12
to publice...@googlegroups.com
Hola a todos,
 
en el grupo generalmente se hacen consultas técnicas, pero pocas veces sobre diseño de datos (me incluyo a la cabeza de la lista). Creo que los programadores asumimos que no necesitamos consultar estas cuestiones porque de una forma u otra vamos a guardar la información y la vamos a encontrar después. Y básicamente es así, pero podemos sacar buenas ideas y copiar mejores prácticas de otros colegas que nos ayudarán a convertirnos en mejores diseñadores y a ahorrarnos dolores de cabeza.
 
Yo estoy ahora exactamente en el punto donde tengo que rediseñar mi sistema por completo, pasarme a un motor SQL y luego, lamentablemente abandonar fox, y probablemente va a ser por Windev. Pero esto es aparte.
 
En este proceso de rediseño y aprendizaje de MySQL, ya que siempre me manejé con tablas nativas, empezaron a surgir serias dudas sobre temas que los tenía absolutamente resueltos. El primero fue el stock. Aquí mis prácticas hasta ahora:
 
1) Para cada producto llevo un historial con un registro por cada evento de entrada/salida, ej. venta, compra, remito interno, corrección, etc. El registro contiene básicamente los campos codigo, fecha, concepto, ingresa, egresa y stock, tal como si fuera una cuenta corriente de ese producto.
 
2) Siempre fuí enemigo de guardar el stock final de un producto en la tabla de productos en la forma codigo - descripcion - precio - stock, sino que por el contrario cuando necesitaba saber el stock de un producto buscaba el último registro de ese producto en el historial y mostraba el valor del campo stock.
 
Con estos dos conceptos me manejé perfectamente, hasta que me llegaron clientes con depósitos y sucursales, y el panorama se complicó. La primera pregunta fue:
 
¿Dónde guardo el historial de cada depósito o sucursal?
 
A) Agrego un campo sucursal y guardo todo en una misma tabla.
B) Tengo cada lugar en una tabla separada.
 
Considerando que se generan cientos o miles de movimientos por día, juntarlos todos en una tabla me traería problemas con el tamaño y con la velocidad de las consultas, por lo que me decidí por la opción B.
 
Ahora bien, algunos clientes quieren conocer el stock en el salón de ventas y en el depósito pequeño de arriba y en el depósito grande que está a 50 metros. Un cliente que vende cubiertas por ejemplo, busca "175 70 13" que es la medida, el sistema le devuelve 10 cubiertas de distintas marcas en esa medida, él no puede estar haciendo clicks en cada producto para saber cuánto hay en cada lugar. El debe saber de un vistazo cuántas cubiertas de cada marca hay en total antes de ofrecerselas a su cliente, no importan dónde estén, por lo tanto necesita el stock global de las mismas. Según el esquema elegido por mí, esto me obliga a hacer una búsqueda en cada historial y sumar el stock final de cada una. Ahora empiezo a pensar que no es tan bueno el diseño. Si la búsqueda no está muy bien hecha, por ejemplo "arandela", el resultado puede contener cientos de registros y por cada registro de ese resultado el sistema tuvo que hacer 3 búsquedas para sumar el stock global, uf.
 
¡Es aquí donde empiezo a pensar que el grupo puede tener una idea diferente (y mejor) al respecto! :-)
 
Ahora, por cuestiones de eficiencia, estoy tentado de hacer lo que siempre me pareció incorrecto, poner el stock final de cada lugar en un campo de la tabla de productos:
 
codigo - descripcion - precio - stock_salon - stock_dep_arriba - stock_dep_grande
 
Cuando uno define un nuevo depósito o sucursal, se crea una nueva columna con ese nombre. Teniendo en cuenta que podemos usar transacciones al ingresar una venta, compra o cualquier documento que mueva stock, estaría garantizado que el campo correspondiente en la tabla de productos muestre el stock final correcto. ¿O no?
 
En conclusión ¿qué harían ustedes? La respuesta debe tener en cuenta que se va a trabajar con MySQL, ya no con tablas nativas.
 
A) ¿Cómo guardarían el historial?
A1) Llevarían un sólo historial para todas las ubicaciones.
A2) Tendrían una tabla para cada ubicación.
A3) No llevarían historial por aparte, sino que lo armarían al momento que el cliente necesite verlo, leyendo los registros de ventas, compras, etc.
 
B) En caso de llevar historial por aparte...
B1) Calcularían el stock en cada registro al momento de crearlo.
B2) Calcularían el stock al momento de mostrar el historial pero sin guardarlo en la tabla.
 
C) En caso de llevar historial y guardar el stock en cada registro...
C1) Guardarían el stock final en el registro del producto.
C2) Lo consultarían tantas veces como ubicaciones hubiere en cada consulta que se haga del producto.
 
Creo que cubrí todo. Como verán, ya que los hice leer bastante, he tratado de por lo menos facilitarles la respuesta, aunque bienvenidos son los comentarios que respalden cada elección. Si me olvidé de alguna alternativa, por favor agréguenla, tienen todavía desde la D hasta la Z.
 
Muchas gracias y espero que sea un intercambio provechoso para todo el mundo.
 
 
Daniel Del Giudice
 
 
 
 
 
 
 

ricardo peña

unread,
Jun 10, 2012, 9:21:54 PM6/10/12
to GRUPO-VFP GRUPO-VFP
Hola Daniel:
 
Si vas a trabajar con una base de datos potente, lo más aconsejable es definir el código
de depósito en una única tabla y recordar poner el código de depósito dentro del indice
donde recorres el archivo.  No sé si mysql los tiene pero puedes usar también índices
filtrados que aceleran mucho el select.  Nunca fuí amigo de guardar el stock en un campo
pues tienes el inconveniente de que si se anula un movimiento, debes recalcularlo en cada
movimiento posterior.  Yo tengo una rutina que me calcula el stock cada vez que se necesita
sumando o restando de acuerdo al código de movimiento.  Si sabes diseñarlo, esta rutina
en una base de datos es muy rápida.
 
Hasta aquí entonces, lo más aconsejable me parece la opción A).  La opción B) descartada
y de la C) la C2).
 
De todas formas, debes considerar el diseño como el eje principal sobre el cuál giran
tus select.  Piensa bien los índices que vas a usar y utiliza las herramientas de la base de datos
para probar ( con el plan de ejecución ) cuál es el mejor camino que esa base de datos
ofrece para tu problema.  El diseño de las tablas y sus índices es clave antes de ponerse
a programar.
 
Ten en cuenta que lo mío es sólo una opinión y puede haber otras.  Piensa, piensa y piensa
pues al fin y al cabo, todo depende de tu tiempo de respuesta.  Tengo bases de datos en sql
server con 1 millón de registros, que calculan el stock en 0.018 segundos.  Eso para tí, ¿ es
rápido o es lento ? Depende.  Depende.
 
Espero haberte sido útil
 
Abrazo 
 
Ricardo Luis Peña
Analista de Sistemas
BA - Argentina
011-15-4440-7378
 

Date: Sun, 10 Jun 2012 17:46:55 -0700
From: daniel...@gmail.com
To: publice...@googlegroups.com
Subject: [vfp] Necesito sus opiniones sobre el diseño de una tabla de stock

Daniel Del Giudice

unread,
Jun 10, 2012, 9:57:50 PM6/10/12
to publice...@googlegroups.com
Gracias por la respuesta. Quedó clarísima, al igual que tu postura. 0.018 segundos en un millón de registros es muuuuy rápido para alguien que trabaja con tablas nativas :-)
 
Algo que olvidé mencionar que hago, es que los registros del historial NO se eliminan, sino que se compensan con otro registro con el concepto correspondiente, ej. si se anula un remito interno, habrá un concepto que diga "Anulación de Remito Interno" y muestre el número de comprobante de origen. Lo mismo para una factura de compra o de venta. Por lo tanto para calcular el stock de un nuevo registro sólo se necesitaría saber el stock del registro anterior para ese producto y para esa ubicación, y sobre eso sumar o restar según corresponda. De esta forma sería más eficiente y más segura la operatoria y el cálculo, no? ¿Qué opinan?
 
Daniel Del Giudice
 
 
 
           

ricardo peña

unread,
Jun 10, 2012, 10:07:37 PM6/10/12
to GRUPO-VFP GRUPO-VFP
Depende Daniel.  Depende.
 
Eso agrega un registro más a la tabla de movimientos y a veces prefiero
"dejar de sumar" ( anular ) que restar del stock 1 registro más.  Todo depende.


Ricardo Luis Peña
Analista de Sistemas
BA - Argentina
011-15-4440-7378
 

Date: Sun, 10 Jun 2012 18:57:50 -0700
From: daniel...@gmail.com
To: publice...@googlegroups.com
Subject: Re: [vfp] Necesito sus opiniones sobre el diseño de una tabla de stock

mpulla

unread,
Jun 10, 2012, 11:29:37 PM6/10/12
to publice...@googlegroups.com

Hola Daniel.

El historial o transaccionalidad la llevo en una sola tabla opción A1

Te describo brevemente las tablas que utilizo

tabla Item.
iItemId Auto Numérido, Codigo_item, descripcion, Unidad de medida, ....

Tabla Departamentos
idpto Auto Numérico, Codigo_Dpto, Descripción

Tabla Item_Dpto
iItemDpto Auto Numérico, iItemId, idpto, nMinimo, nMaximo, Ubicación,.....

Tabla Cabecera_Movimientos
iMovCId, Fecha, TipoMov, .....

Tabla Detalle_Movimientos
iMovDId, iMovCId, iItemDpto, nCantidad, nCosto, nTotal, nCant (Positivo o Negativo), nTot (Positivo o Negativo)

Cuando necesito saber saldo a una fecha dada, solamente tengo que hacer un Sum(nCant)  sin importar el tipo de movimiento que sea.

Al igual que la mayoria de nosotros no me gusta la Idea de guardar el stock en un campo, en mi caso utilizo Sql Server y para saber el Stock utilizo una vista Indexada.

Lo que comenta Ricardo sobre los indices filtrados, suena bien, a demas de tener buenos tiempos.

Ricardo puedes dar un ejemplo de tu indice filtrado?

Saludos.
Mauricio

ricardo peña

unread,
Jun 11, 2012, 6:24:51 AM6/11/12
to GRUPO-VFP GRUPO-VFP
 
Tiro como idea la buena noticia de que nuestro querido zorro tiene índices filtrados. Se
puede consultar la ayuda que está muy clara la explicación.
 
Para sql server se puede consultar la ayuda de sql server 2012 Books On Line (BOL)
 
Hay algunas consideraciones de diseño que hay que tener.
 
Aquí va el código
 
CREATE NONCLUSTERED INDEX FIProductosConFechaVto
    ON Production.Productos (ProdID, FechaIni)
    WHERE FechaVto IS NOT NULL ;
GO
 
El índice filtrado es válido para la consulta siguiente.
 
SELECT ProdID, ProdDescrip, FechaIni
FROM Production.Productos
WHERE FechaVto IS NOT NULL 
    AND FechaIni > @FecInicial
GO

Cuidado que sólo funciona para sql server 2012.

 
Ricardo Luis Peña
Analista de Sistemas
BA - Argentina
011-15-4440-7378
 

Date: Sun, 10 Jun 2012 20:29:37 -0700
From: jmaur...@yahoo.es
To: publice...@googlegroups.com
Subject: [vfp] Re: Necesito sus opiniones sobre el diseño de una tabla de stock

Carlos Miguel FARIAS

unread,
Jun 11, 2012, 8:30:42 AM6/11/12
to publice...@googlegroups.com
Si vas a rediseñar la bd, serìa bueno que aplicaras normalización
sobre la misma, en general, una buena normalización logra un sistema
eficiente, luego, en casos puntuales, una "desnormalización" o la
creación de campos espécificos, pueden lograr respuestas más
eficientes.
Desde mi punto de vista contable, tener los movimientos de stock
desagregados es mejor, y en principio, toda operación que tuvo algún
efecto, debería ser restaurada con un contra movimiento, la anulación
de registros solo es pertinente ante transacciones fallidas o no
concretadas o que no haya causado efecto sobre los estados del
sistema.
P.E.
Venta A -- Registra venta de 3 productos - se hacen los registros de
movimientos de stock
Si antes de entregarse la mercadería, o hacerse el remito u otro
documento asociado, se anula algún producto, si se podría anular el
registro correspondiente, pero si, se hizo un remito, u otra operación
antes de la devoluciòn, podría darse que una Venta B rechazo algun
item, porque en su instante, no había stock.
Si anulas/borras el movimiento de la Venta A, va a parecer (al hacerse
control) que el sistema falló al hacer la Venta B, porque sin un
registro especifico, el sistema dijo que no habia stock y en los
"papeles" si.
Si queda un registro y el contraregistro, estos tendrìan sus fecha y
horas, por lo que podría saberse que al momento de la Venta B, el
sistema no tenía stock.
O sea, es necesario tener en cuenta a que nivel se quiere llevar el control.
MySql no tiene indices filtrados, pero podes crear una vista sobre la
tabla y luego acceder a la vista. Para un recupero eficiente, si la
frecuencia de consultas es alto, es conveniente poner un indice sobre
el/los campo/s que se utilicen como filtro.
Saludos: Miguel, La Pampa (RA)

El 11/06/12, ricardo peña <rl...@hotmail.com> escribió:
>
>
> Tiro como idea la buena noticia de que nuestro querido zorro tiene índices
> filtrados. Se
> puede consultar la ayuda que está muy clara la explicación.
>
> Para sql server se puede consultar la ayuda de sql server 2012 Books On Line
> (BOL)
>
> Hay algunas consideraciones de diseño que hay que tener.
>
> Aquí va el código
>
> CREATE NONCLUSTERED INDEX FIProductosConFechaVto
> ON Production.Productos (ProdID, FechaIni)
> WHERE FechaVto IS NOT NULL ;
> GO
>
> El índice filtrado es válido para la consulta siguiente.
>
> SELECT ProdID, ProdDescrip, FechaIni
> FROM Production.Productos
> WHERE FechaVto IS NOT NULL
> AND FechaIni > @FecInicial
> GO
>
> Cuidado que sólo funciona para sql server 2012.
>
> Ricardo Luis Peña
> Analista de Sistemas
> BA - Argentina
> 011-15-4440-7378
>
>
>
>

Daniel Del Giudice

unread,
Jun 11, 2012, 9:04:56 AM6/11/12
to publice...@googlegroups.com
¡Perfecto! Una nueva idea que ni siquiera se me cruzó alguna vez por la cabeza. Gracias.
 
D) Historial sin el cálculo del stock registro a registro (eso ahorra ya 2 campos de la tabla)
 
 
Me gustaría saber si todos los que utilizan SQL coinciden en que no es un trabajo excesivo sumar todos los registros de un producto al momento de querer saber su stock, teniendo en cuenta que pueden ser miles o decenas de miles. Al parecer sí coinciden. Yo, con mi mentalidad "dbf" yo todavía lo veo como un tremendo trabajo puesto sobre el servidor.
 
Daniel Del Giudice

ricardo peña

unread,
Jun 11, 2012, 9:13:12 AM6/11/12
to GRUPO-VFP GRUPO-VFP
Daniel.
 
Todo puede funcionar bien.  Es necesario meterse en la mentalidad del
sql server y saber cómo ejecutará una instrucción select.  Para eso hay
mucha ayuda con el plan de ejecución de una select.  Lo que debes
tener en cuenta es el índice y por supuesto la select debe estar en un
procedimiento almacenado dado que de lo contrario el sql debe "pensar"
el plan de ejecución cada vez que invocas la sentencia select. 


Ricardo Luis Peña
Analista de Sistemas
BA - Argentina
011-15-4440-7378
 

Date: Mon, 11 Jun 2012 06:04:56 -0700
From: daniel...@gmail.com

To: publice...@googlegroups.com
Subject: [vfp] Re: Necesito sus opiniones sobre el diseño de una tabla de stock

Daniel Del Giudice

unread,
Jun 11, 2012, 9:33:19 AM6/11/12
to publice...@googlegroups.com
Gracias Miguel. Yo también prefiero no borrar registros y tenerlo como una referencia central de lo que pasa en el negocio.
 
Daniel Del Giudice
 
 
 
 

Daniel Del Giudice

unread,
Jun 11, 2012, 9:40:47 AM6/11/12
to publice...@googlegroups.com
Gracias Ricardo. Los procedimientos almacenados es algo nuevo que sé que debo implementar a full cuando migre a MySQL. Si bien no sabía bien por qué, me acabas de dar la clave ;-)
 
Daniel Del Giudice
           

Carlos Miguel FARIAS

unread,
Jun 11, 2012, 12:15:17 PM6/11/12
to publice...@googlegroups.com
Si te preocupa la cantidad de registros a analizar para detectar la
existencia actual, es muy simple. Contablemente, se debe hacer un
inventario anual, en ese momento, se debe ajustar existencia
informática a existencia real, en ese punto, todos los movimientos
"antiguos" o se transfieren/mueven a una tabla " histórica"
(borrandola de la original), entonces de esa manera los registros a
recorrer son los del último año.
Si no queres tener una tabla separada, creas un campo de estado, que
diga "antiguo" y haces una vista que filtre antiguo. Si te dicen que
no hacen inventario anual, podes instrumentar un proceso por demanda,
cada vez que se constata una existencia real, eso se carga como
"cantidad real", todos los movimientos anteriores se mueven, o
"antigüizan" y no los consideras para el calculo de existencia
disponible.
Son opciones, deberás ver que te conviene mas.
Saludos: Miguel


El 11/06/12, Daniel Del Giudice <daniel...@gmail.com> escribió:

Guillermo MDQ

unread,
Jun 11, 2012, 2:25:45 PM6/11/12
to publice...@googlegroups.com
Yo para estos casos no marco ni grabo en otra tabla los movimientos como historicos.
Tengo una tabla llamada Inventarios en donde grabo cada inventario que se realiza con su fecha correspondiente.
Asi cuando calculo el stock lo hago a partir del ultimo inventario existente en esa tabla para ese local y le sumo o resto los movimientos a partir de la fecha del inventario correspondiente.

Saludos
Guillermo

Carlos Miguel FARIAS

unread,
Jun 11, 2012, 8:33:57 PM6/11/12
to publice...@googlegroups.com
Esa es otra opción totalmente válida, las posibilidades son muchas, lo importante es que las tablas sean lo más especificas posibles, a los efectos de que no contengan datos no correspondientes.
Varias veces he comentado que al hacer los diseños, lo más importante en todo es reducir el volumen de datos, y eso se logra con una buena normalización y el uso del tipo de datos correcto.
Los sgbd son muy potentes, pero si nuestros diseños son malos, es dificil que logren cumplir su cometido.

Saludos: Miguel, La Pampa (RA)


Daniel Del Giudice

unread,
Jun 11, 2012, 11:30:31 PM6/11/12
to publice...@googlegroups.com
Gracias Miguel. Es otra idea buena que no se me había ocurrido. En el caso mío son negocios que no necesariamente hacen un inventario anual, sino más bien van controlando aisladamente o por grupos sus productos, pero vale igual, ya que al hacer una corrección manual, el sistema asume que todo lo que estaba antes no necesita sumarlo más, marcándolo para que no se incluya en futuros cálculos. Perfecto, si voy combinando las distintas ideas creo que tenemos un resultado fantástico.
 
Daniel Del Giudice
 
 
 

Daniel Del Giudice

unread,
Jun 11, 2012, 11:45:12 PM6/11/12
to publice...@googlegroups.com
Gracias Guillermo por responder. Entiendo entonces que cada vez que debes saber el stock de un producto buscas el último inventario y a partir de ahí le sumas las compras, ya sea que hayan entrado por remitos o facturas, restas las devoluciones a proveedores, restas las ventas, restas los remitos a clientes, sumas las notas de crédito a clientes y sumas o restas los movimientos entre depósitos o sucursales. Perdón que detalle todo pero quería asegurarme que ese es el proceso que realmente haces, recorrer las distintas tablas donde están registradas entradas o salidas. En definitiva, va a ser la misma cantidad de registros que va a tener que sumar si estuvieran todos juntos en un historial, pero al sumar los distintos tiempos de cada búsqueda ¿no se te va muy arriba?
 
Daniel Del Giudice
 
 
 

Guillermo MDQ

unread,
Jun 12, 2012, 12:33:43 AM6/12/12
to publice...@googlegroups.com
Hola Daniel, hago el proceso tal cual lo detallaste, a partir del ultimo inventario le sumo o resto de acuerdo a los movimientos correspondientes de entrada o salida.
Y para lograr buenos tiempos de respuesta me aseguro de tener los indices correctos que coincidan con las claves utilizadas en los where de los selects.

Saludos
Guillermo

Walter R. Ojeda Valiente

unread,
Jun 12, 2012, 1:49:49 AM6/12/12
to publice...@googlegroups.com
Yo prefiero utilizar una sola tabla para todos los movimientos, diferenciando el tipo de movimiento por un código, ejemplos:
ECM = Entrada por compras
EDV = Entrada por devolución del cliente
ETR = Entrada por traslado desde otra Sucursal
SIN = Salida por incendio
SRO = Salida por robo
STR = Salida por traslado a otra Sucursal
SVT = Salida por ventas
y varios más.

De esta manera me resulta muy sencillo realizar cualquier consulta.

Todos los productos tienen una columna "Fecha del stock inicial" y una columna "Cantidad inicial", para saber la cantidad exacta que existía de ellos cuando se empezó a utilizar mi aplicación. Adicionalmente tengo una tabla llamada INVENTARIOS donde se registran las cantidades físicas contadas cuando se hicieron los inventarios.

Para saber la cantidad actual de un producto, hallo la fecha de su último inventario (de no existir, uso la fecha del stock inicial) y le sumo todas las cantidades cuyo código de tipo de movimiento empieza con "E" y le resto todas las cantidades cuyo código de tipo de movimiento empieza con "S".

Saludos.

Walter.





Date: Mon, 11 Jun 2012 20:45:12 -0700
From: daniel...@gmail.com
To: publice...@googlegroups.com
Subject: Re: [vfp] Re: Necesito sus opiniones sobre el diseño de una tabla de stock

Miltiño

unread,
Jun 12, 2012, 3:55:53 AM6/12/12
to Comunidad de Visual Foxpro en Español
Bueno Yo lo hago así en SQL Server, para empresas con Multibodega

Tabla: Productos
Codigo, Nombre, PrecioA, PrecioB, CostoProm

Tabla: Detalle de Facturas
FacturaId, Bodega, Codigo, Cantidad, Costo, Precio

Tabla: Kardex (Registra todas las transacciones Compras, Ventas,
Salidas, Entradas, Transferencias entre bodegas, ...)
OrigenTrans, NumeroTrans, TipoTrans, Bodega, Codigo,Fecha, Cantidad,
Costo, Precio, CostoProm

Tabla: Stock
Bodega, Código, Cantidad

En este esquema tengo que resaltar lo siguiente:
1.- Cuando se guarda una factura, el sistema tiene implementado
triggers para que automáticamente desencadene una transacción en la
tabla Kardex, haciendo todos los calculos necesarios de costo
promedio, actualización de existencias, etc, etc
2.- La tabla Kardex tiene implementada otros Triggers que en el
momento en que se inserta una transacción, automáticamante llama a
actualizar la tabla de Stock ajustando el valor de la cantidad en
existencias en la bodega y producto en cuestión, con esto tengo el
stock calculado y disponible en todo momento.
3.- La tabla Stock tiene que tener los indices de bodega y Codigo,
esto permitirá acelerar la consulta. Por ejemplo, si tenemos unos 5000
productos y tenemos 3 bodegas, a lo mucho esta tabla tendra 15000
registros y la búsqueda será practicamene instantánea.
4.- El costo promedio se recalcula solamente cuando existen
transacciones posteriores a la fecha en que se está insertando, y se
deposita el costo promedio actual en la tabla de productos, dejando
disponible para su consulta, esta tabla tendrá a lo mucho 5000
registros, con un buen índice sobre el código, esto será rápido.
5.- La tabla Kardex deberá tener indices por: OrigenTrans,
NumeroTrans, TipoTrans, Bodega, Codigo,Fecha, esto permite obtener el
kardex, hacer recalculo de costos u otro tipo de transacción
rápidamente.

Espero te sirva.

Att.


MILTON

Guillermo MDQ

unread,
Jun 12, 2012, 9:54:13 AM6/12/12
to publice...@googlegroups.com
Walter, con la forma que usas de guardar la fecha de stock inicial en cada producto, cuando haces la consulta para que te de el stock, lo puedes resolver con un select o tenes que recorrer los productos para ir tomando las distintas fechas de inicio ?

Saludos
Guillermo

Daniel Sánchez

unread,
Jun 12, 2012, 11:48:14 AM6/12/12
to publice...@googlegroups.com
Interesante lo de actualizar tu stock con un desencadenador al momento de la inserción y podrías mostrarte cual es el código de tu desencadenador para calcular y actualizar el stock debo suponer que dicha referencia esta en tu tabla de stock para consulta rápida.

Saludos

--
Daniel Sánchez Escobar
Investigación y Desarrollo
Reset Software & Sistemas
Móvil +051-949398047
Trujillo - Perú

Daniel Del Giudice

unread,
Jun 12, 2012, 12:13:51 PM6/12/12
to publice...@googlegroups.com
Gracias Walter y Milton por sus respuestas.
 
 
Esto hilo es todo lo que había pensado y más. No sé si ustedes están sacando buenas ideas o van a cambiar su diseño (lo cual siempre implica mucho trabajo), pero en lo que a mi respecta he tomado excelentes ideas que las estoy combinando para llegar a lo que me parece mejor para mi sistema, además de permitirme echar una mirada a la forma de trabajo de ustedes, lo cual siempre es interesante y enriquecedor. Gracias.
 
Daniel Del Giudice 
           

Walter R. Ojeda Valiente

unread,
Jun 12, 2012, 12:41:01 PM6/12/12
to publice...@googlegroups.com
Hola Guillermo

Con un solo SELECT se puede resolver, algo como:

  lcConsulta = "SELECT P.PRD_IDENTI, " ;
             + "       P.PRD_CANINI, " ;
             + "       COALESCE((SUM(CASE WHEN MC.MVC_TIPMOV = 'ECM' THEN MD.MOV_CANTID END)), 0) AS COMPRAS, " ;
             + "       COALESCE((SUM(CASE WHEN MC.MVC_TIPMOV = 'SVT' THEN MD.MOV_CANTID END)), 0) AS VENTAS " ;
             + "  FROM MOVIMDET MD " ;
             + "  JOIN MOVIMCAB MC ON MD.MOV_CODSUC = MC.MVC_CODSUC AND MD.MOV_IDENTI = MC.MVC_IDENTI " ;
             + "  JOIN PRODUCTOS P ON MD.MOV_CODSUC = P.PRD_CODSUC  AND MD.MOV_IDEPRD = P.PRD_IDENTI " ;
             + " WHERE MD.MOV_CODSUC = " + Transform(tnCodSuc) + " AND MD.MOV_IDEPRD = " + Transform(tnIdePrd) + " " ;
             + "GROUP BY P.PRD_IDENTI, P.PRD_CANINI"

Allí obtengo, para cada producto, su cantidad inicial, sus compras y sus ventas.

Saludos.

Walter.





Date: Tue, 12 Jun 2012 06:54:13 -0700
From: gol...@gmail.com
To: publice...@googlegroups.com
Subject: [vfp] Re: Necesito sus opiniones sobre el diseño de una tabla de stock

Guillermo MDQ

unread,
Jun 12, 2012, 1:19:33 PM6/12/12
to publice...@googlegroups.com
El select se entiende perfecto Walter, lo que no me queda claro es a partir de que fecha empezas a tomar los movimientos de tu tabla MOVIMDET ya que no veo ningun campo de fecha inicial.

Saludos
Guillermo



El martes, 12 de junio de 2012 13:41:01 UTC-3, Walter R. Ojeda Valiente escribió:
Hola Guillermo

Con un solo SELECT se puede resolver, algo como:

  lcConsulta = "SELECT P.PRD_IDENTI, " ;
             + "       P.PRD_CANINI, " ;
             + "       COALESCE((SUM(CASE WHEN MC.MVC_TIPMOV = 'ECM' THEN MD.MOV_CANTID END)), 0) AS COMPRAS, " ;
             + "       COALESCE((SUM(CASE WHEN MC.MVC_TIPMOV = 'SVT' THEN MD.MOV_CANTID END)), 0) AS VENTAS " ;
             + "  FROM MOVIMDET MD " ;
             + "  JOIN MOVIMCAB MC ON MD.MOV_CODSUC = MC.MVC_CODSUC AND MD.MOV_IDENTI = MC.MVC_IDENTI " ;
             + "  JOIN PRODUCTOS P ON MD.MOV_CODSUC = P.PRD_CODSUC  AND MD.MOV_IDEPRD = P.PRD_IDENTI " ;
             + " WHERE MD.MOV_CODSUC = " + Transform(tnCodSuc) + " AND MD.MOV_IDEPRD = " + Transform(tnIdePrd) + " " ;
             + "GROUP BY P.PRD_IDENTI, P.PRD_CANINI"

Allí obtengo, para cada producto, su cantidad inicial, sus compras y sus ventas.

Saludos.

Walter.





Date: Tue, 12 Jun 2012 06:54:13 -0700
From: gol...@gmail.com

Walter R. Ojeda Valiente

unread,
Jun 12, 2012, 1:26:09 PM6/12/12
to publice...@googlegroups.com
Tienes razón Guillermo, me faltó agregar la columna de la fecha del stock inicial en los CASE.

Saludos.

Walter.




Date: Tue, 12 Jun 2012 10:19:33 -0700
From: gol...@gmail.com
To: publice...@googlegroups.com
Subject: Re: [vfp] Re: Necesito sus opiniones sobre el diseño de una tabla de stock

Miguel Antúnez

unread,
Jun 12, 2012, 3:43:35 PM6/12/12
to publice...@googlegroups.com
en  mi caso para el tema de stock manejo dos tablas básicas, que obviamente se complementan con mas.

detallare solo el detalle de movimientos y el stock

mi tabla de movimientos 
id_codalm
id_mov
id_prod
id_tipomov
fecha_mov
Cantidad
**************Las cantidades se guardan positivo los ingresos, negativo las salidas

tabla de stock
id_codalm
id_prod
Stock
*****************stock actualizado al ultimo movimiento

en la tabla de movimientos tengo un trigger
 
update "TABLA STOCK" set STOCK= "TABLA STOCK".STOCK+tm.CANTIDAD
from ((select ID_PROD,id_codalm,sum(CANTIDAD) as cantIDAD FROM inserted group by id_prod,id_codalm)) tm
where  "TABLA STOCK" .id_prod=tm.id_prod and  "TABLA STOCK".id_codalm=tm.id_codalm

hice algunos cambios para no complicar el ejemplo, ya que en mi caso uso lotes; pero en el fondo es lo mismo. 

no uso saldos iniciales, los calculo en base a estas dos tablas con una simple suma de los movimientos de la fecha que quiero como inicial y sumar de la "tabla stock"

Saludos.

 







--
Miguel Angel Antúnez Camones
mant...@gmail.com


Miltiño

unread,
Jun 12, 2012, 5:17:15 PM6/12/12
to Comunidad de Visual Foxpro en Español
ESTE TRIGGER VA EN LA TABLA DETALLE DE FACTURA (RENFAC)

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[RENFAC_INS]
ON [dbo].[RenFac]
FOR INSERT
AS
BEGIN
-- ACTUALIZAR EL KARDEX
-- Preparando datos necesarios antes de insertar en el Kardex
DECLARE @Cliente int, @FacturaId int, @Cuenta_cc int, @CostoProm
money, @Saldo numeric(10,5),
@RenFac_id int, @TipoIVA numeric(1), @TipoPRO numeric(1),
@Desglose numeric(10,5),
@Usuario char(10), @TerminalId char(20)
DECLARE @Bodega numeric(2), @Emision_rf smalldatetime, @Importe_rf
money,
@Referencia varchar(25), @Codigo char(16), @Unidades
char(10), @Cantidad numeric(10,5),
@CostoUni money, @CostoTot money, @PrecioTot money
DECLARE Items CURSOR FOR
SELECT RenFac_id, Bodega, Emision_rf, FacturaId, Referencia,
Codigo, Unidades,Cantidad, TipoIVA, TipoPRO, Importe_rf
FROM INSERTED WHERE TIPO_RF='' -- Extraer Solo items del
inventario
OPEN Items
FETCH NEXT FROM Items INTO @RenFac_id, @Bodega, @Emision_rf,
@FacturaId, @Referencia, @Codigo, @Unidades,
@Cantidad, @TipoIVA, @TipoPRO, @Importe_rf

SELECT @Cuenta_cc = Cuenta_cc,@Usuario=Usuario,
@TerminalId=TerminalId FROM ENCFAC WHERE FacturaId = @FacturaId

WHILE @@FETCH_STATUS = 0
BEGIN
-- Calculando Costo Promedio y Precio sin IVA
SELECT @CostoProm = dbo.KARCostoProm(@Codigo,@Emision_rf)
SELECT @CostoUni = @CostoProm
SELECT @CostoTot = @CostoProm*@Cantidad
SELECT @Desglose = 1+ CASE @TipoIVA WHEN 1 THEN 0.12 ELSE 0
END + CASE @TipoPRO WHEN 1 THEN 0.10 ELSE 0 END
SELECT @PrecioTot = @Importe_rf/@Desglose
SELECT @Cantidad = @Cantidad*-1
SELECT @Saldo = dbo.KARSaldo(@Codigo,@Emision_rf) +
@Cantidad
-- Fin Calculando
UPDATE RENFAC
SET PrecioTot = @PrecioTot, CostoTot = @CostoTot
WHERE RENFAC_ID=@RenFac_Id
EXEC dbo.KARInsertarTrans @Bodega,
@Emision_rf,'FAC',@FacturaId, @Referencia,@Cuenta_cc,2, @Codigo,
@Unidades,
@Cantidad, @CostoProm, @CostoTot,
@PrecioTot,@Saldo,@Usuario,@TerminalId
FETCH NEXT FROM Items INTO @RenFac_id, @Bodega, @Emision_rf,
@FacturaId, @Referencia, @Codigo, @Unidades,
@Cantidad, @TipoIVA, @TipoPRO, @Importe_rf
END
CLOSE Items
DEALLOCATE Items
-- FIN ACTUALIZAR EL KARDEX

END


EL TRIGGER DE LA TABLA KARDEX, SOBRE LA TABLA DE STOCK (BODEXI)

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[KARDEX_INS]
ON [dbo].[Kardex]
FOR INSERT
AS
BEGIN
-- ACTUALIZAR EXISTENCIAS DE BODEGAS (BODEXI) de todos los
registros insertados
DECLARE @Bodega numeric(2), @Codigo char(16), @Cantidad
numeric(10,5)
DECLARE Trans CURSOR FOR
SELECT Bodega, Codigo, Cantidad FROM INSERTED
OPEN Trans
FETCH NEXT FROM Trans INTO @Bodega, @Codigo, @Cantidad
WHILE @@FETCH_STATUS = 0
BEGIN
IF EXISTS(SELECT * FROM BODEXI WHERE Bodega=@Bodega AND
Codigo=@Codigo)
UPDATE BODEXI SET Cantidad = Cantidad + @Cantidad WHERE
Bodega=@Bodega AND Codigo=@Codigo
ELSE
INSERT INTO BODEXI (Bodega,Codigo, Cantidad) VALUES
(@Bodega, @Codigo, @Cantidad)
FETCH NEXT FROM Trans INTO @Bodega, @Codigo, @Cantidad
END
CLOSE Trans
DEALLOCATE Trans
-- FIN ACTUALIZAR EXISTENCIAS DE BODEGA
END

mpulla

unread,
Jun 12, 2012, 11:44:12 PM6/12/12
to publice...@googlegroups.com

Hola Miltiño.

Los Gurús de Sql Server dicen que se debe evitar el uso de cursores por su bajo rendimiento, viendo tus triggers, si usas Sql Server 2008 o superior me animo a pedirte que pruebes este código a lo mejor te sirve para reemplazar el cursor.

Si lo pruebas comenta como te fue

Saludos.
Mauricio

P.D. Aclaro que estoy aprendiendo Sql Server.

--TRIGGER [dbo].[KARDEX_INS] ON [dbo].[Kardex]

IF OBJECT_ID (N'dbo.BODEXI', N'U') IS NOT NULL
    DROP TABLE dbo.BODEXI;
GO

CREATE TABLE dbo.BODEXI (Bodega Int,Codigo nvarchar(3), Cantidad decimal (10,2));
GO

INSERT INTO dbo.BODEXI VALUES(1, 'A', 20), (1, 'B', 26), (1, 'E', 30);
GO

IF OBJECT_ID (N'dbo.BodInserted', N'U') IS NOT NULL
    DROP TABLE dbo.BodInserted;

GO

CREATE TABLE dbo.BodInserted (Bodega Int,Codigo nvarchar(3), Cantidad decimal (10,2));
GO

INSERT INTO dbo.BodInserted VALUES (1, 'C', 27), (1, 'D', 28), (1, 'G', 29), (1, 'A', 240);
GO

MERGE dbo.BODEXI AS Target
USING (SELECT Bodega, Codigo, Cantidad  FROM BodInserted) AS Source
ON (Target.Bodega = Source.Bodega AND Target.Codigo = Source.Codigo)
WHEN MATCHED THEN
    UPDATE SET Target.Cantidad = Target.Cantidad + Source.Cantidad
WHEN NOT MATCHED BY TARGET THEN
    INSERT (Bodega,Codigo, Cantidad)
    VALUES (Source.Bodega, Source.Codigo, Source.Cantidad)
    OUTPUT $action, Inserted.*;

Select * From BODEXI

DROP TABLE dbo.BODEXI;
DROP TABLE dbo.BodInserted

Miltiño

unread,
Jun 13, 2012, 2:56:53 PM6/13/12
to Comunidad de Visual Foxpro en Español
Gracias Mauricio:

Lo dejare para probar, el problema es que no puede subir a la ver.
2008 por cuestiones de integración, tengo que mantenerme, ya lo había
analizado, existen también la posibiliad de usar variables temporales
de tabla que vienen en la ver. 2008. En cuanto pueda revisaré tu
sugerencia.

Att.


MILTON

Daniel Sánchez

unread,
Jun 13, 2012, 6:12:12 PM6/13/12
to publice...@googlegroups.com
Que tal Miltiño, lo que veo es que tienes una tabla bodexi donde tienes la referencia del producto y un campo donde vas actualizando la referencia del stock (en mi caso lo hago directo desde el programa), pensé que harías una suma de los movimientos en kardex de dicho producto obteniendo el stock, de esta manera nunca estaría desfasado, debo suponer que realizas lo mismo cuando se hace un update o un delete del movimiento del kardex.

Miltiño

unread,
Jun 14, 2012, 10:34:33 AM6/14/12
to Comunidad de Visual Foxpro en Español
Exactamente Daniel, debe implementarse los triggers de Update y
Delete. Como podrás darte cuenta, todo esto hace que se tenga el
control del stock y el Kardex directamente en la Base de Datos,
haciendo que el proceso de grabación sea más rápido sin
complicaciones. Otra ventaja que se tiene es que por ejemplo si quiero
eliminar una factura y todas las transacciones involucradas, solamente
ejecuto el comando DELETE ENCFAC WHERE ENCFAC_ID=102, de esta manera
el trigger que tengo implementado en ENCFAC elimina las transacciones
de RENFAC, el trigger de borrado de RENFAC elimina la transacción en
el KARDEX y el trigger de borrado del Kardex Actualiza el Stock de
bodega, a continuación te envío los triggers para que los veas.

---1.- Trigger de Encabezado de Factura ENCFAC
ALTER TRIGGER [dbo].[ENCFAC_DEL]
ON [dbo].[EncFac]
FOR DELETE
AS
BEGIN
-- AL ELIMINAR FACTURA, ELIMINAR: RENFAC
DECLARE @FacturaId int
SELECT @FacturaId = FacturaId FROM DELETED
DELETE RENFAC WHERE FacturaId=@FacturaId
-- FIN AL ELIMINAR FAC....
END

--- 2.- Trigger de Renglon de factura RENFAC
ALTER TRIGGER [dbo].[RENFAC_DEL]
ON [dbo].[RenFac]
FOR DELETE
AS
BEGIN
-- ELIMINAR DEL KARDEX, TODAS LAS LINEAS INVOLUCRADAS EN EL
BORRADO
DELETE KARDEX WHERE Kardex_id IN (SELECT Kardex_Id FROM DELETED
WHERE Kardex_id!=0)

END

-- 3.- Trigger de Kardex
ALTER TRIGGER [dbo].[KARDEX_DEL]
ON [dbo].[Kardex]
FOR DELETE
AS
BEGIN
-- ACTUALIZAR EXISTENCIAS DE BODEGAS (BODEXI) de todos los
registros insertados
DECLARE @Bodega numeric(2), @Codigo char(16), @Cantidad
numeric(10,5)
DECLARE Trans CURSOR FOR
SELECT Bodega, Codigo, Cantidad FROM DELETED
OPEN Trans
FETCH NEXT FROM Trans INTO @Bodega, @Codigo, @Cantidad
WHILE @@FETCH_STATUS = 0
BEGIN
IF EXISTS(SELECT * FROM BODEXI WHERE Bodega=@Bodega AND
Codigo=@Codigo)
UPDATE BODEXI SET Cantidad = Cantidad + @Cantidad*-1
WHERE Bodega=@Bodega AND Codigo=@Codigo
ELSE
INSERT INTO BODEXI (Bodega,Codigo, Cantidad) VALUES
(@Bodega, @Codigo, @Cantidad*-1)

Carlos Miguel FARIAS

unread,
Jun 14, 2012, 3:58:13 PM6/14/12
to publice...@googlegroups.com
Y no olvides backup backup backup backup...
porque si se rompe la bd, se te rompe la lógica del sistema

Saludos: Miguel, La Pampa (RA)

Daniel Sánchez

unread,
Jun 14, 2012, 9:59:16 PM6/14/12
to publice...@googlegroups.com
Esta excelente, en tu caso veo que manejas la unidades y fracciones en un solo campo, el inconveniente o limitación de este método es que solo trabajas o controlas cantidades en notación decimal o base 10 osea cada 10 fracciones suma o resta una unidad, en mi caso manejo separado unidades y fracciones, dando la facilidad de manejar incrementos de unidades para fracciones de pares, docenas o cualquier presentación que uno desee sin mayor problema, es decir que si tengo un producto que se vende por docenas significa que una docena es una unidad, pudiendo vender una unidad que es igual a una docena o vender las fracciones y determina que cada 12 suma o resta una unidad, cosa que con el otro método los incrementos son por cada diez fracciones incremento o decremento, en mi caso el calcular el nuevo stock es un poco más complejo que sumar o restar a+b o a-b para obtener el nuevo stock.
En todo caso lo que me gustaría que me mostraras un pequeño cambio en tu lógica (es que no manejo mucho el tema de los SP), en lugar de trabajar con el valor que esta siendo grabado para calcular el nuevo stock, si no es mucha molestia como seria realizando la sumatoria del kardex del producto de las entradas y salidas para obtener el nuevo stock al vuelo, lo que quiero es probar esto mismo realizándolo desde el sistema y esto realizado de manera automática desde un triggers como en tu caso y ver la diferencia de velocidad en ambos casos.

Daniel Sánchez

unread,
Jun 14, 2012, 10:02:38 PM6/14/12
to publice...@googlegroups.com
Así es Carlos M. si toda la lógica de negocio esta depositada en la base de datos, pues se daña tu bd y te quedas con medio sistema o medio de nada. Como todo en la vida tiene sus pro y sus contras, si no la tienes todo en la bd puedes más fácil o con cambios mínimos (si se trabaja con el estándar SQL) cambiar entre diversos motores de BD. En realidad nunca se me ha presentado pero creo que lo podría encarar con cambios mínimos.
Reply all
Reply to author
Forward
0 new messages