El problema que tuve la semana pasada con transacciones y lo comento por si
alguien mas lo sufre...

es que tengo un procedimiento almacenado que me realiza el bloqueo de una OT
en el motor y mientras hago el calculo con esa ot otro usuario no la tendría
que poder tomar...

Hasta ahi todo bien... lo ponia dentro de una transaccion con este seteo
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

y se supone que otro usuario no debería poder utilizar mi misma OT hasta que
no termine mi transacción.

Pero resulta que no es tan así.

Para estar totalmente seguro que sea así hay que utilizar un parametro
adicional el primer select que se hace sobre la tabla a bloquear...

*with (UpdLock)*

este UpdLock impide que si entra al mismo tiempo otra transacción solo gane
una y la tabla se desbloquea al terminar la transacción.

Si bien soluciona el problema del bloqueo, genera luego un problema nuevo
denominado interbloqueo en la cual la transacción que pierde debe ser
reenviada al motor para que la procese.

Este tema lo comento como punta.... pero si se les presento lo vemos
puntualmente.

Saludos,

Pancho
Cordoba

El 13 de junio de 2011 17:51, Rafael Copquin <[email protected]>escribió:

>  Como ya lo anticipé, recurriendo a las HCS lo saqué, tanto para SQL
> PassThrough como para cursor adapters
>
> Esto al principio lo mandé como off topic, pero dado que se trata de VFP
> con SQL Server, es más topic que nunca. Aquí va la solución:
>
> a) usando SPT (sql pass through)
>
> Hacemos la conexión y la capturamos en nHandle,  por ejemplo:
>
> nHandle = sqlstringconnect([dsn=miconexion;uid=sa;pwd=rafael])
>
> ** comienza la transacción
>
> SQLExec(nHandle,'BEGIN TRANSACTION')
> SQLSetProp(nHandle,'Transactions',2)     && ponemos las transacciones del
> SQLServer en manuales
>                                                                       &&
> esto hay que hacerlo DESPUES del begin transaccion, si no, no anda
>
> cCmd = [insert into cabecera(campo1,campo2,etc) values(valor1,valor2,etc)]
> nResultado = SQLExec(nHandle,cCmd)
>
> lOK = iif(nResultado > 0,.t.,.f.)
>
> if lOK
>
>     ** si la grabación en la tabla cabecera anduvo bien, obtenemos la clave
> primaria generada (identity key)
>
>     nPK      = 0
>     nResults = 0
>     cCmd     = [select @@IDENTITY as pk ]
>
>     lBatchMode = SQLGetProp(nHandle,"BatchMode" )
>
>     SQLSetProp(nHandle,"BatchMode" ,.f.)
>
>     SQLExec(nHandle,cCmd,'curPK')
>
>     Do while nResults <> 2
>        nResults = SQLMoreResults(nHandle)
>     enddo
>
>     SQLSetProp(nHandle,"BatchMode" ,lBatchMode)
>
>     nPK = curPK.pk
>     Use in Select('curPK')
>
>    ** ahora insertamos en la tabla de detalle cada uno de los registros y
> usamos nPK como clave foránea
>
>    select curFactura         &&  es  un cursor local que tiene todas las
> líneas de detalle de la factura
>    scan all while lOK
>            cCmd = [insert into detalles(campo1,campo2,FK,etc)
> values(curFactura.campo1,curFactura.campo2,nPK,etc)]
>            nResultado = SQLExec(nHandle,cCmd)
>            lOK = iif(nResultado > 0,.t.,.f.)
>    endscan
>
> endif
>
>
> If lOK
>    SQLCommit(nHandle)               && si todo anduvo bien, mandamos el
> commit de la transacción
> Else
>    SQLRollback(nHandle)               && si algo anduvo mal, mandamos el
> rollback
> endif
> SQLSetProp(nHandle,'Transactions',1)          && volvemos a poner las
> transacciones en automático
>
> b) con cursor adapters
>
> Generamos dos cursor adapters vacíos para la cabecera y el detalle (el
> filtro es where 1=0)
> (curCabecera y curDetalles)
> El connection handle es el mismo para ambos cursores
>
> ** comenzamos la transacción como antes, poniéndolas en manual
>
> SQLExec(nHandle,'BEGIN TRANSACTION')
> SQLSetProp(nHandle,'Transactions',2)
>
> Con los cursor adapters usamos tableupdate para grabar los insert ( no
> olvidar de poner la propiedad BufferModeOverride = 5, o sea buffering
> optimista de tablas y la propiedad SendUpdates = .t. )
>
> insert into curCabecera(campo1,campo2, etc) values(valor1,valor2,etc)
> lOK = TableUpdate(1,.t.,'curCabecera')
>
> if lOK
>
>     nPK   (obtenemos el identity generado, con el mismo código que se ve en
> el caso de SPT)
>
>    ** ahora insertamos en la tabla de detalle cada uno de los registros y
> usamos nPK como clave foránea
>
>    select curFactura         &&  es  un cursor local que tiene todas las
> líneas de detalle de la factura
>    scan all while lOK
>           insert into curDetalles(campo1,campo2,FK,etc)
> values(curFactura.campo1,curFactura.campo2,nPK,etc)]
>           lOK = TableUpdate(1,.t.,'curDetalles')
>    endscan
>
> endif
>
> If lOK
>    SQLCommit(nHandle)               && si todo anduvo bien, mandamos el
> commit de la transacción
> Else
>    SQLRollback(nHandle)               && si algo anduvo mal, mandamos el
> rollback
> endif
> SQLSetProp(nHandle,'Transactions',1)          && volvemos a poner las
> transacciones en automático
>
> Les cuento que gasté muchas HCS hasta que me di cuenta de que las
> transacciones se ponen en manual después de mandar el Begin Transaction. De
> lo contrario anda mal, o sea, si hay un error, graba todas las tablas antes
> del error y por supuesto la que está con error no las graba.
>
> Y no importa si la conexión se hace con ODBC o con ADO, porque eso no tiene
> nada que ver.
>
> That's all folks !!! :-)
>
> Rafael Copquin
>
>
> El 24/05/2011 14:15, Rafael Copquin escribió:
>
> Ya lo lei, pero sigue la confusión. Voy a experimentar y seguro que por
> prueba y error lo saco. (mis famosas HCS :-) )
>
>
> El 24/05/2011 12:44, francisco prieto escribió:
>
> Rafa,
>
>  Me parece que el temita esta por este link...
>
>  http://msdn.microsoft.com/en-us/library/ms131281(v=SQL.100).aspx
>
>  Saludos,
>
>  Pancho
> Cordoba
>
> El 24 de mayo de 2011 12:26, Rafael Copquin <[email protected]>escribió:
>
>>  La conexión es la misma. No se cambia durante la transacción. Y por ahora
>> estoy usando ODBC, no ADO.
>>
>> Lei por ahi que al mandar el comando begin transaction via sqlexec
>>
>> sqlexec(nHandle,'BEGIN TRANSACTION')
>>
>> el ODBC pone automáticamente en MANUAL  la transacción ( le manda el valor
>> 1 ) ¿es asi?
>>
>> Rafael Copquin
>>
>>
>> El 24/05/2011 9:28, Omar Bellio escribió:
>>
>>   Rafa, lo que te debe estar pasando es que la conexión que usás para los
>> CursorAdapters es distinta a la que usa el SqlExec, por lo tanto, las
>> transacciones y las otras instrucciones “no están viendo el mismo canal”,
>> por así decirlo.
>>
>>
>>
>> La solución de Pancho, como podrás ver, usa la misma conexión ADO para
>> toda la operatoria entonces no tiene dramas.
>>
>>
>>
>> Otra observación, me parece que si cambiás tu bloque de instrucciones Scan
>> Alll… EndScan por una sola instrucción Insert into… Select… from… vas a
>> tener mejor rendimiento (aunque los registros sean poquitos, me parece más
>> elegante, pero esto es sólo una cuestión de gustos)
>>
>>
>>
>> Salute!
>>
>>
>>
>> *De:* [email protected] [mailto:[email protected] <[email protected]>] *En
>> nombre de *francisco prieto
>> *Enviado el:* martes, 24 de mayo de 2011 08:38 a.m.
>> *Para:* GUFA List Member
>> *Asunto:* [GUFA] OFF TOPIC TRANSACT SQL - manejo de transacciones
>>
>>
>>
>> Rafa,
>>
>>
>>
>> No es que la tenga tan clara sino que me crucé con tantos quilombos en
>> implementaciones que decidí no utilizar cursoradapters, sino una clase
>> propia a la que la llame rstocursor... pero eso es harina de otro costal y
>> si queres en otro momento la vemos.
>>
>>
>>
>> Utilizo, gracias a esta clase ADODB y para trabajar con transacciones
>> simplemente hago esto:
>>
>>
>>
>> oConexionActual.Execute("SET IMPLICIT_TRANSACTIONS OFF")
>>
>>
>>
>> oConexionActual.Execute("BEGIN TRANSACTION")
>>
>>
>>
>> Luego por ejemplo hago un insert en el  motor de la siguiente forma
>>
>>
>>
>>                                    lcSql="Insert Into ColaEspera
>> (Orden_cEsp,F_Llegada,Nro_Turno,Estado,Flg_Espera,IdActuante,IdDerivad)
>> VALUES ("+;
>>
>>
>> "'"+cOrden.Nro_Turno+"',"+;
>>
>>                                                "'"+lcLlegada+"',"+;
>>
>>
>> "'"+cOrden.Nro_Turno+"',"+;
>>
>>                                                "'ES',"+;
>>
>>                                                "'P',"+;
>>
>>                                                "NULL"+","+;
>>
>>
>> "'"+cOrden.IdDerivador+"')"
>>
>>                                    Try
>>
>>
>> oConexionActual.Execute(lcSql)
>>
>>                                    Catch To loError
>>
>>                                                llHuboError=.T.
>>
>>                                                lcTexto="La instruccion
>> "+Chr(13)+Chr(10)+;
>>
>>
>> lcSql+Chr(13)+Chr(10)+;
>>
>>                                                            "emitio el
>> error "+Alltrim(Str(loError.ErrorNo))+" - "+loError.Message
>>
>>                                                
>> Strtofile(Transform(Date(),"@E")+"
>> "+Ttoc(Datetime(),2)+" "+_Screen.UsrName+" ("+;
>>
>>                                                            
>> ALLTRIM(_Screen.AppName)+")
>> "+lcTexto,_Screen.DirApp+_Screen.MisErrores.ArchLog,1)
>>
>>                                    Endtry
>>
>>
>>
>>
>>
>> y finalmente, cuando termino con todo...
>>
>>
>>
>> If llHuboError
>>
>>             oConexionActual.Execute("ROLLBACK TRANSACTION")
>>
>>             Messagebox("Se produjo un error interno durante la
>> grabación.",16,"Avise a Sistemas")
>>
>> Else
>>
>>             oConexionActual.Execute("COMMIT TRANSACTION")
>>
>> Endif
>>
>>
>>
>> Esta operatoria nunca me causo los errores que vos mencionas....
>>
>>
>>
>> Tengo otros quilombos (quien no? :)), pero hasta el momento con SqlServer
>> fueron todas sonrisas...
>>
>>
>>
>> Saludos,
>>
>>
>>
>> Pancho
>>
>> Cordoba
>>
>> El 24 de mayo de 2011 08:24, Rafael Copquin <[email protected]>
>> escribió:
>>
>> Ahora yo tengo una pregunta sobre SQL Server: es el manejo de
>> transacciones, que es diferente de la forma en que VFP las maneja.
>>
>>
>> Veamos el ejemplo clásico de una factura. Dos tablas, una cabecera y otra
>> de detalle.
>>
>> Yo genero dos cursor adapters vacíos con este código
>>
>> local cCmd
>>
>> cCmd = [select * from cabecera where 1=0]
>> y genero el CA 'curCabecera' && tengo una clase que genera CA , pero es un
>> código largo que no viene al caso
>>
>> cCmd = [select * from detalles where 1=0]
>> y genero el CA 'curDetalles'
>>
>> Para hacerla corta, al grabar hago algo así
>>
>> cCmd = [begin transaction]
>> sqlexec(nHandle,cCmd) && con lo que le mando al SQL la orden de iniciar la
>> transacción
>> Luego
>>
>>    insert into curCabecera ........
>>   lOK = tableupdate(.t.,.t.,'curCabecera')
>>   if lOK
>>      select curFactura && este es un cursor local que opera con una grilla
>>      scan all
>>            scatter name oFac
>>            insert into curDetalles from name oFac
>>      endscan
>>      lOK = tableupdate(.t.,.t.,'curDetalles')
>>   if lOK
>>     cCmd = 'IF @@TRANCOUNT > 0 COMMIT'
>>   else
>>    cCmd = 'IF @@TRANCOUNT > 0 ROLLBACK'
>>  endif
>>   sqlexec(nHandle,cCmd)
>>
>> Pero sé que a esto le falta una pata, porque si provoco un error a
>> propósito en la grabación, para que la segunda tabla no se grabe, por
>> ejemplo), la segunda tabla no se graba pero la primera se graba igual.
>>
>> Intenté usar transacciones manuales antes del begin transaction
>> #DEFINE DB_TRANSMANUAL  2
>> SQLSetProp(nHandle, "TRANSACTIONS", DB_TRANSMANUAL)
>>
>> y luego volverlas automáticas después del end transaction
>> #DEFINE DB_TRANSAUTO    1
>> SQLSetProp(nHandle, "TRANSACTIONS", DB_TRANSAUTO)
>>
>> Pero esto hace que los cursor adapters no graben nada
>>
>> Me gustaría ver qué mecanismo usan Uds para manejar esto. A lo mejor el
>> hecho de que estoy usando cursor adapters cambia las cosas respecto de hacer
>> comandos que graben directamente en las tablas del SQL Server. Después de
>> todo, un CA es como una vista de VFP, con propiedades y métodos propios,
>> pero vista al fin.
>>
>> Pero como Pancho parece que la tiene clara, la pregunta es para vos (o
>> cualquier otro que también la tenga clara)
>>
>> Rafael Copquin
>>
>>
>>
>>
>>
>

Responder a