bloqueo de registro

716 views
Skip to first unread message

Rene Jara

unread,
May 7, 2013, 11:42:01 AM5/7/13
to publicesvfoxpro
Hola a todos los foxeros
como puedo bloquear un registro en una tabla de SQL...esto con el proposito de controlar
consecutivo de la factura... cuando hay mas de 1 usuario haciendo facturacion.
si existe un ejemplo me lo pueden indicar
 
cordialmente y agredeciendo su valiosa ayuda a todos
 
Rene jara
 

Arnaldo Toledano

unread,
May 7, 2013, 12:24:26 PM5/7/13
to publice...@googlegroups.com
Porque BLOQUEAR EL REGISTRO ???
Ojo, no te compliques.
Yo conozco solo bloquear TABLAS, no registros.
Pero si pretendes hacer eso, tenes que prever que pasa si otro usuario necesita las tablas y están bloqueadas.
Por no decir, si por algún motivo ese Usuario DEJO bloqueada las tablas sin darse cuenta y después vienen
los problemas con los otros usuarios.


En mi caso, lo que realizo es OBTENER el próximo nro. de comprobante a partir de una tabla que los contiene.
Actualizo la tabla_nros_comprobante  con el NUEVO NUMERO, para el próximo usuario que lo requiera.
Grabo todas las tablas con el NUMERO OBTENIDO, y punto.


Arnaldo Toledano
--
Arnaldo Toledano Tesys Informática Córdoba Argentina

Víctor Hugo Espínola Domínguez

unread,
May 7, 2013, 12:28:23 PM5/7/13
to publicesvfoxpro
Hola René

Busca en el foro, 15-abril-2013 

[OT - Consulta]Analogía de Rlock de VFP en SQL Server

Hace mucho tiempo que estoy buscando una respuesta concreta y satisfactoria a esta pregunta y no la encuentro, no sé si porque nadie sabe o no quieren compartir.

La solución provisoria que adopté es tener la tabla de consecutivos en dbf en un directorio compartido del servidor, tiene el inconveniente de que al estar en un directorio compartido no es muy seguro. Esa debilidad puedes subsanarla mediante un esquema de permisos a usuarios y/o grupos bien diseñados.

Esperemos que esta vez alguien nos de una respuesta.

Saludos,
Víctor.

Ing.Daniel Bojorge

unread,
May 7, 2013, 12:52:59 PM5/7/13
to publice...@googlegroups.com
Yo para esto estoy utilizando una mezcla de:
  • Iniciar variable dentro de una instrucción
  • Instrucción a usar es update (SQL bloquea automáticamente el registro afectado)
  • Nivel de aislamiento

Con esas tres cosas, creo que se podría hacer el bloque de registro. 



Dios L@s Bendiga

Saludos,

Daniel (Con 1 Estrella DCE de Microsoft)
www.debsconsultores.com
Nicaragua

"Si ustedes permanecen unidos a mí, y si permanecen fieles a mis enseñanzas, pidan lo que quieran y se les dará.
(Juan 15:7 DHH)
Bendito el varón que se fía en el SEÑOR, y cuya confianza es el SEÑOR.
(Jeremías 17:7 RV2000)

Ing.Daniel Bojorge

unread,
May 7, 2013, 12:58:57 PM5/7/13
to publice...@googlegroups.com
Me refiero a esto:

Imágenes integradas 1

Hay diferentes niveles de aislamiento, yo uso uno no tan bueno (READ COMMITTED), pero me ha funcionado, olvidé que el nivel de aislamiento es de una transacción.

Además ahí marqué cómo incrementar el valor de un campo y guardarlo en una variable, todo en una sola instrucción en sql server.


Dios L@s Bendiga

Saludos,

Daniel (Con 1 Estrella DCE de Microsoft)
www.debsconsultores.com
Nicaragua

"Si ustedes permanecen unidos a mí, y si permanecen fieles a mis enseñanzas, pidan lo que quieran y se les dará.
(Juan 15:7 DHH)
Bendito el varón que se fía en el SEÑOR, y cuya confianza es el SEÑOR.
(Jeremías 17:7 RV2000)


Isolation.jpg

Antonio Meza

unread,
May 7, 2013, 1:01:18 PM5/7/13
to publice...@googlegroups.com
Hola!!

Uso Mysql y al igual que lo uso en VFP con tablas nativas, tengo una tabla idfacturas donde solo tengo un campo ID y otro folio, y de esta forma bloqueo la tabla para obtener el siguiente registro de factura, puede haber muchas formas pero es lo mas practico y sobre todo que ha funcionado perfectamente.

El bloqueo a la tabla lo hago al momento de guardar el registro de factura, de esta forma la tabla solo queda bloqueada unos milisegundos en lo que se realiza la operación del usuario y si 2 usuarios intentan obtener el folio de factura a uno le muestra un mensaje de espere unos segundos... o registro ocupado intente de nuevo y listo jejej

La tabla IdFactura no la uso en ningún otro formulario por lo que no afecta que este bloqueada unos instantes, esta misma acción aplico para los Cheques, es decir tengo otra tabla llamada IdCheques, y así aplico tabla por registro consecutivo que necesito asignar, es una forma sencilla, practica y que me resuelve el problema.

saludos
Antonio Meza


mpulla

unread,
May 7, 2013, 4:27:58 PM5/7/13
to publice...@googlegroups.com
Hola Victor Hugo.

Uso Sql Server 2012 Expressy lo hago de la siguiente forma.

Tengo un SP que surte de secuencias algo muy parecido a  Números conecutivos para nuetras tablas

ALTER PROCEDURE [App].[SecuenciaSimpleSec_spUI] @iKey int, @cDescripcion nVarChar(50) = NULL, @iKeyNext INT OUTPUT
AS
SET NOCOUNT ON

Begin Try

 DECLARE @R TABLE (
 iSecId int NOT NULL PRIMARY KEY,
 iSecuencia INT NOT NULL CHECK (iSecuencia > 0)
 );

INSERT INTO @R (iSecId, iSecuencia)
SELECT
    z.iSecId, z.iSecuencia
FROM
    (
    MERGE App.SecuenciaSimple WITH (HOLDLOCK) AS T
    USING (SELECT @iKey, @cDescripcion) AS S (iKey, cDescripcion)
    ON T.iSecId = S.iKey
    WHEN MATCHED THEN
        UPDATE SET iSecuencia = iSecuencia + 1
    WHEN NOT MATCHED THEN   
        INSERT (iSecId, cDescripcion, iSecuencia)
        VALUES (S.iKey, S.cDescripcion, 1)
    OUTPUT
        $ACTION AS [action], INSERTED.iSecId, INSERTED.iSecuencia
    ) AS z;


SELECT @iKeyNext = iSecuencia FROM @R WHERE iSecId = @iKey;

End try

Begin Catch
    Throw;
end catch

Como ejemplo SP para crear Paises donde utilizo el sp App.SecuenciaSimpleSec_spUI

CREATE Procedure [App].[Pais_spI] @Nombre nVarchar(50), @cAbr nVarchar(3), @iKeySec Int, @iPaisId smallint Out
As
Set Nocount On;

Begin Try
 Begin Transaction
   
    Exec App.SecuenciaSimpleSec_spUI @iKeySec, 'Pais', @iKeyNext = @iPaisId  Out
   
    INSERT INTO App.Pais
    (iPaisId, cNombre, cAbr, iDBAudId)
    VALUES
    (@iPaisId, @Nombre, @cAbr, @iDBAudId)

 Commit Transaction
End try

Begin Catch
 if @@TRANCOUNT > 0
    Rollback Transaction;
 Throw;
 end Catch

El truco este en el hint del sp SecuenciaSimpleSec_spUI, es decir en:

 MERGE App.SecuenciaSimple WITH (HOLDLOCK) AS T

HOLDLOCK Equivalente a SERIALIZABLE

Espero te sea de ayuda.

Saludos.
Mauricio

Víctor Hugo Espínola Domínguez

unread,
May 8, 2013, 7:24:30 PM5/8/13
to publicesvfoxpro
Hola Mauricio

Abusando de tu buena voluntad, me gustaría que mires el código adjunto. Si tienes alguna observación o corrección será muy apreciada.

Saludos,
Víctor.



2013/5/7 mpulla <jmaur...@yahoo.es>
NuevoIdSql.txt

mpulla

unread,
May 8, 2013, 11:46:05 PM5/8/13
to publice...@googlegroups.com
Hola Victor.

Es mejor crearse un SP ya que Sql Server almacena estadísticas, además de almacenarlos en memoria, lo que hace que la ejecución se mas rápida, para esto
créate la tabla de secuencias (TablaId), luego el SP que te devuelva la siguiente secuencia y finalmente lo llamas desde VFP.

No dices con que versión de Sql Trabajas, este ejemplo te va a funcionar por lo menos de la version 2000 en adelante.

Create Procedure NuevoIdSql_sp @NomTabla varchar(30), @iKeyNext INT OUTPUT
AS
SET NOCOUNT ON
SET TRANSACTION ISOLATION SERIALIZABLE

UPDATE TablaId SET iKeyNext = UltValId  = UltValId + 1 WHERE NomTabla = @NomTabla

IF @@ROWCOUNT = 0
   Begin
     Insert Into TablaId (NomTabla, UltValId) Values (@NomTabla, 1)
     Set @iKeyNext = 1
   end



Despues de declarada la transacción

Private lcNomTabla As String, liSecuencia As Integer
lcNomTabla = 'MiTabla'
SQLExec(tnConexion, [NuevoIdSql_sp ?lcNomTabla, ?@liSecuencia])

listo ya tienes tu nuevo Id.
El nivel de aislamiento SERIALIZABLE es el más restrictivo por lo que la transacción tiene que durar lo menos posible, o si quieres puedes usar un nivel de aislamiento menos restrictivo.

Saludos.
Mauricio


Víctor Hugo Espínola Domínguez

unread,
May 9, 2013, 12:19:53 AM5/9/13
to publicesvfoxpro
Hola Mauricio

Gracias por el código, me gustó la doble asignación.

Yo sé que los SP son más eficientes, pero quiero tener el control mediante  código del lado de VFP para que el sistema sea lo más genérico posible, sin importar cual SGBD sea utilizado. Además en un programa de ABM que es donde se genera el nuevo Id. es irrelevante la velocidad de generación.

Es más un capricho que otra cosa, pues aprender la sintaxis de los SP en diversos motores es una pavada y fácilmente uno puede tener una biblioteca de SP para todos los SGBD en una tabla de configuración.

Mis dudas son:
- Es importante el cambio de tipo de transacción automática a manual o no?
- Si hay requerimiento simultáneo de nuevo id. entre dos usuarios, el que "perdió la carrera" espera espera sin problemas o recibe un mensaje de error?
- En definitiva el código que te envié es una buena simulación del RLOCK() del VIsual Foxpro o no?

Saludos,
Víctor.


Ing.Daniel Bojorge

unread,
May 9, 2013, 10:40:43 AM5/9/13
to publice...@googlegroups.com
El ejemplo que te puse es muy parecido al de Mauricio, lo mejor es usar SP de la Base de Datos, esto para que tu sistema sea más "genérico" en el sentido que podés hacer módulos en VFP y en VS Net, es más hasta podés hacer una aplicación web (siempre en VS Net) que use la base de datos y como el control de bloqueo está en el SGBD, sólo es utilizarlo.  

Te comento esto porque ya me pasó, inicié un proyecto de un sistema en VFP 5, desde vfp generaba unas claves "encriptadas" y las guardaba en SQL Server, pero por motivos de movilidad (necesitaba algo para que se ejecutasen en los teléfonos), quise hacer una aplicación web, pero ahí me topé con que las claves de usuarios estaba "encriptadas" pero con vfp y VB 2008 (lo que usé) no podría "leer" esa ecriptación, además que el bloqueo de registros lo hacía VFP, así que tuve reescribir todo eso, pero esta vez al ponerlo en el SGBD, logré hacer que funcione igual, ya sea desde vfp o VB 2008 (ya sea una aplicación de escritorio, una aplicación web o incluso desde unos pda).



Dios L@s Bendiga

Saludos,

Daniel (Con 1 Estrella DCE de Microsoft)
www.debsconsultores.com
Nicaragua

"Si ustedes permanecen unidos a mí, y si permanecen fieles a mis enseñanzas, pidan lo que quieran y se les dará.
(Juan 15:7 DHH)
Bendito el varón que se fía en el SEÑOR, y cuya confianza es el SEÑOR.
(Jeremías 17:7 RV2000)


Roberto Tello & Asoc. - Estudio Informatico

unread,
May 9, 2013, 11:32:05 AM5/9/13
to publice...@googlegroups.com

En Oracle lo hacemos asi, y no hemos tenido problema de repeticion:

SP de Oracle:

--Procedure: PROC.NUEVOITEM

--DROP PROC.NUEVOITEM;

CREATE OR REPLACE PROCEDURE PROC.NUEVOITEM (
    IDNUMERO IN OUT NUMBER
)
AS
BEGIN
   SELECT MAX(numero)+1 into idnumero FROM boletas;
   INSERT INTO boletas (numero,detalle) values (idnumero,'');
   commit;
END;

Y desde VFP lo invocamos asi:

z=SQLEXEC(gnConnHandle,"{CALL NUEVOITEM(?@idnumero)}")

idnumero es una variable... y recible el valor de retorno del SP con el numero incrementado, ya que la misma en el SP es IN OUT.

De esta manera no hace falta bloqueo, porq cada  SP invocado debe ejecutarse por completo hasta que entre la proxima invocacion del mismo, el cual queda como en cola para su ejecucion.

Espero ayude... habria que adaptarlo similar en mysql...

Sdos.-

Roberto.-

Reply all
Reply to author
Forward
0 new messages