| |  |  | Miembros: Mensajes: Temas: Online: Ultimo Miembro: | | |  | | | 
| 
29-06-2006, 10:44:05
| | Gran Participación en el Foro | | Registrado: may 2006 Posts: 108
| | Orden de ejecución de sentencias SQL Buenos días gente, tengo un pequeño quebradero de cabeza. Os cuento, desde un código VB quiero lanzar dos sentencias SQL consecutivas contra un sql, en concreto dos UPDATE a dos tablas diferentes. En estas tablas quiero actualizar un campo FECHA, con horas y segundos.
Bien, lanzo la primera query y a continuación la segunda. El problema que tengo es que la primera consulta es un UPDATE de muchos registros, mientras que la segunda es sólo un UPDATE de un único registro. Lo que me pasa es que como la primera query es más pesada, tarda un poco más que la segunda, con lo que el campo fecha de la segunda se rellena con una fecha que es 1-2 segundos anterior a la de la primera query a pesar de haber sido lanzada posteriormente.
Esto es un problema porque luego hay lógica de programa que asume que dicha fecha no puede ser anterior a las otras, como mucho igual o posterior.
Mi pregunta es, ¿hay alguna forma de asegurarse de que el segundo UPDATE no se hace hasta que no ha terminado el primero?. Puesto que el objeto recordset no devuelve ningún valor (no es como un SELECT donde Visual Basic no sigue ejecutando código hasta que no nos han llegado los datos) el Visual Basic lanza la consulta y sigue ejecutando código sin esperar a que el sql haya procesado completamente la query, por lo que enseguida se encuentra la segunda sentencia UPDATE que también lanza y acaba antes de que haya terminado la primera.
No sé si me he explicado bien. He intentando buscar cosas en Google sobre este tema, pero no he logrado encontrar nada ya que tampoco sé bien cómo buscar de forma efectiva una respuesta. Lo poco que he encontrado me habla del paralelismo de ejecución del sql, pero nada sobre cómo modificarlo o poder alterarlo de alguna forma según se necesite, o si hay alguna forma de secuenciar las queries (con una transacción tengo el mismo problema).
Saludos y muchas gracias. | 
29-06-2006, 20:00:18
|  | Administrator | | Registrado: dic 2002 Ubicación: BURGOS - ESPAÑA Posts: 5.266
| | Solución: Creo que no te fallará Código: Dim FechaActual As Date
FechaActual = Date Y actualizas la fecha de ambas tablas con el valor de FechaActual. Y, por favor, no te des un golpe en el coco por no haber caído en la cuenta, que a veces lo más simple se nos atraviesa como una viga y no cabe por la puerta. | 
30-06-2006, 06:32:08
| | Gran Participación en el Foro | | Registrado: may 2006 Posts: 108
| | Hola José María, gracias por la respuesta. Sería una buena opción, pero olvidé indicar en mi mensaje que es una base de datos a la que accederán simultáneamente diferentes usuarios desde diferentes ordenadores por lo que la fecha debe guardarse en base a la fecha del servidor, no puedo guardar la fecha local del PC porque no tendría garantías de que todas las fechas de todos los PCs estuvieran sincronizadas (nada impediría a un usuario tener cualquier fecha en su PC) y me daría incoherencias temporales entre los datos de distintos usuarios, de ahí que tenga que usar la función GetDate() del sql.
Saludos | 
30-06-2006, 07:06:03
|  | Moderador | | Registrado: mar 2004 Ubicación: BARCELONA - ESPAÑA Posts: 973
| | Hola, que tal.
En un programa que tengo de Control Horario de Personal, cogo la fecha del servidor y no de los distintos pc,s.
Más o menos hago lo siguiente:
GetRemoteTime servidor ' Servidor es el nombre de tu servidor: ejemplo: \\ServidorPrincipal
rs2.Fields("HORAENTRADA") = TimeSerial(t_struct.tod_hours, t_struct.tod_mins - _
t_struct.tod_timezone, t_struct.tod_secs)
rs2.Fields("HORASALIDA") = TimeSerial(t_struct.tod_hours, t_struct.tod_mins - _
t_struct.tod_timezone, t_struct.tod_secs)
rs2.Fields("FECHA") = DateSerial(t_struct.tod_year, t_struct.tod_month, _
t_struct.tod_day)
Y en el módulo tengo:
Type TIME_OF_DAY_INFO
tod_elapsed As Long
tod_msecs As Long
tod_hours As Long
tod_mins As Long
tod_secs As Long
tod_hunds As Long
tod_timezone As Long
tod_tinterval As Long
tod_day As Long
tod_month As Long
tod_year As Long
tod_weekday As Long
End Type
Public rs1 As ADODB.Recordset
Public rs2 As ADODB.Recordset
Public t_struct As TIME_OF_DAY_INFO
Public hora As Date
Public servidor As String
Public Declare Function NetRemoteTOD Lib "netapi32.dll" (yServer As Any, pBuffer As Long) As Long
Private Declare Function NetApiBufferFree Lib "netapi32.dll" (ByVal pBuffer As Long) As Long
Private Declare Sub CopyMem Lib "kernel32.dll" Alias "RtlMoveMemory" (pTo As Any, uFrom As Any, ByVal lSize As Long)
Public Function GetRemoteTime(ServerName As String) As Date
'Devuelve la fecha y la hora de una máquina específica de la red.
' En este caso del servidor que está en variable servidor.
Dim lpBuffer As Long
Dim ret As Long
Dim bServer() As Byte
On Error GoTo EtiqError
If Trim(ServerName) = "" Then
'Máquina Local
ret = NetRemoteTOD(vbNullString, lpBuffer)
Else
If InStr(ServerName, "\\") = 1 Then
bServer = ServerName & vbNullChar
Else
bServer = "\\" & ServerName & vbNullChar
End If
ret = NetRemoteTOD(bServer(0), lpBuffer)
End If
CopyMem t_struct, ByVal lpBuffer, Len(t_struct)
If lpBuffer Then
Call NetApiBufferFree(lpBuffer)
End If
GetRemoteTime = DateSerial(t_struct.tod_year, t_struct.tod_month, _
t_struct.tod_day) + TimeSerial(t_struct.tod_hours, t_struct.tod_mins - _
t_struct.tod_timezone, t_struct.tod_secs)
' TimeSerial lleva la información de la hora.
' DateSerial lleva la informción del día.
Exit Function
EtiqError:
GetRemoteTime = Now
MsgBox "Error en captura hora " & ServerName
On Error GoTo 0
End Function
Con todo esto consigo que a la hora de "fichar" siempre obtenga los datos del servidor:
No se si te he liado más o te sirve la idea de coger la hora directamente del servidor, lanzando las SQL a distintas horas.
Espero que te sirva en algo y no me haya dejado nada importante. | 
30-06-2006, 07:53:44
|  | Administrator | | Registrado: dic 2002 Ubicación: BURGOS - ESPAÑA Posts: 5.266
| | Mantengo, erre que erre, mi propuesta, sólo que si necesitas tomar la fecha del servidor, no tomes la fecha del PC del cliente sino la del servidor. Pero siempre que ejecutes los dos procesos referenciados a una variable en que tengas guardada la fecha del sistema evitarás que ésta se modifique por la demora de ejecución de esos procesos. | 
03-07-2006, 06:35:31
| | Gran Participación en el Foro | | Registrado: may 2006 Posts: 108
| | Gracias por vuestras sugerencias, son una aproximación mejor que la actual a mi problema, pero sigue manteniéndose parte de mi problema ya que si bien con vuestra idea de coger la fecha del servidor evito el problema de que el "timestamp" de la primera sea mayor que el de la segunda a pesar de haber sido lanzadas en orden inverso, no evito el problema de que dicho "timestamp" sea incorrecto ya que el momento en que yo leo la fecha del servidor no me garantiza que sea el momento en que el proceso haya concluido. Me explico:
Tengo las dos querys, la A es la primera y la B la segunda, ambas son UPDATES (uno masivo y otro simple):
Supongamos que en el instante 00:00,00 lanzo la primera query, la A, que es la más pesada y que lleva más tiempo Instante 1: 00:00,00 Lanzo Query A
Supongamos que la query termina en el instante 00:08,00 Instante 2: 00:07,00 Fin Query A
Puesto que no tengo forma de saber en qué momento ha terminado la query, la fecha del servidor la habré cogido o en el instante previo a lanzar la query A, o en algún instante entre el Instante 1 y el Instante 2 suponiendo que tan pronto lanzo la query he hecho la consulta de fecha. A lo que se sumaría que la query B habría sido lanzada también en algún instante entre el Instante 1 y el Instante 2 y a su vez tendría un "timestamp" aproximado, no exacto.
No sé si me he explicado bien con el ejemplo. Realmente lo que necesito es alguna forma de poder controlar en qué momento termina un proceso lanzado contra el servidor SQL. Algo que me permita tener control sobre el momento en que se inician y finalizan las consultas, que me garantice que la segunda sentencia no se ha lanzado antes de que haya terminado la primera. Es decir, poder secuenciar las consultas de alguna forma, que no se ejecuten en paralelo.
Mis conocimientos de SQL y bases de datos son bastante reducidos, por lo que a lo mejor estoy pidiendo algo ilógico o innecesario que se puede abordar de otra forma o que quizá me encuentro con este problema debido a un mal planteamiento de la lógica de mi programa, pero a priori me parece una situación que no debe ser atípica por lo que entiendo que alguna forma de control del flujo debe de haber en el tema de las consultas SQL.
Saludos | 
03-07-2006, 14:29:18
|  | Administrator | | Registrado: dic 2002 Ubicación: BURGOS - ESPAÑA Posts: 5.266
| | No sé si estoy quedándome en otro plano o si es que no soy capaz de hacerme entender.
Si lo que pretendes es grabar la fecha y hora en que realizas los dos procesos, y necesitas que esa fecha sea exactamente igual en ambos, ¿tan difícil es entender que la única fórmula de garantizar que la hora no varíe es pasar el dato a una variable y luego utilizar esa variable como fecha y hora para grabar en ambos procesos? Como soy incapaz de entender por qué no se considera adecuada mi propuesta, ruego que alguien me explique el por qué, para que deje de dar guerra, aunque prometo que ya no volveré a insistir... Pero me gustaría saber en qué estoy metiendo la pata... | 
03-07-2006, 21:41:34
| | Gran Participación en el Foro | | Registrado: may 2006 Posts: 108
| | Hola José María, entiendo tu propuesta, posiblemente quien no se está haciendo entender soy yo. Voy a intentar explicarme mejor.
Mi problema es que tengo una serie de queries de SQL (en este ejemplo son dos, pero podrían ser N) que se ejecutan una detrás de otra. Todas esas queries deben llevar el timestamp en que se procesan y han de ser ejecutadas de forma secuencial, es decir, la query 2 no puede ser ejecutada antes de la query 1 haya terminado y por lo tanto el timestamp de la query 2 no podrá ser en ningún caso menor que la primera, como mucho igual en caso que la query 1 termine tan rápido que ambos timestamps coincidan (precisión de segundos necesito, aunque para las pruebas estoy usando décimas de segundo también).
De esta forma, puesto que dichas queries son UPDATES o INSERTS, el flujo de código de Visual Basic no se queda esperando como pueda pasar en un SELECT (a que lleguen los datos), por lo que manda la primera query y el flujo de Visual sigue mientras la query se ejecuta en el servidor. Esto da lugar a que si la primera query es grande o si el servidor está saturado y sufre retardo, se lance la siguiente query sin haber terminado la primera.
La opción que propones de leer la fecha/hora del servidor antes de lanzarlas me serviría parcialmente si la hago una vez justo antes de cada query e inserto a mano el campo fecha como parte del INSERT o el UPDATE, pero no solucionaría el problema de que la lectura de la fecha/hora del servidor para la segunda query se haya realizado una vez haya terminado la primera query porque no tengo forma de saber cuándo ha terminado de realizarse la primera.
Gráficamente de lo que me pasa actualmente (eje X -> tiempo, eje Y -> lanzamiento de queries, suponemos que el timestamp lo pillo del servidor al inicio de cada query), es decir, cada query se lanza sin haber esperado a que su predecesora haya terminado puesto que no tengo forma de saber cuándo lo ha hecho (en azul los timestamps que me devuelve el servidor a cada consulta de fecha/hora y que son los que se grabarían en la BBDD):
query1
[00:00----------------------00:06]
_______query2
_______[00:03---------------------00:07]
____________query3
____________[00:04------------00:07]
_____________________query4
_____________________[00:06-------------------------00:09]
Y esto es lo que necesitaría (en naranja los timestamps correctos):
query1___________________query2___________________ query3_____________query4 00:00----------------------00:06][00:06---------------------00:10][00:10------------00:13][00:13-------------------------00:16]
Espero haberme explicado mejor, insisto en que no soy ningún entendido en SQL y BBDD y es posible que el origen de mi problema esté en no estar usando alguna facilidad que tenga el lenguaje SQL (¿transacciones quizá?).
Saludos y muchas gracias por el tiempo que os estoy haciendo perder.
PD: realmente mi pregunta se podría generalizar: ¿hay alguna forma de secuenciar las queries donde no se solapen sus ejecuciones?. Olvidando que mi problema actual es debido a timestamps, en una situación donde se necesite que las queries (de la naturaleza que sea) se ejecuten en un orden concreto y secuencial, ¿cómo afrontar esa situación?. | 
03-07-2006, 22:32:14
|  | Administrator | | Registrado: dic 2002 Ubicación: BURGOS - ESPAÑA Posts: 5.266
| | Pues bendito sea Dios, que al fin me desayuno... Desde tu primer mensaje he interpretado que querías que todas las consultas quedaran grabadas con la misma fecha... Y me estaba volviendo majara con mis dudas sobre si era que yo no hablaba en castellano...
Yo me he encontrado con un problema diferente, pero semejante, puesto que me encontré con errores secundarios a que pretendía usar un fichero que todavía no había terminado de actualizarse. Una orden SQL aparentemente es instantánea, pero lógicamente obliga al sistema a dedicar posteriormente un tiempo para que se graben las actualizaciones y se cierre el fichero modificado. Pues terminé haciendo una rutina que, ojalá nadie me la califique de demencial, aunque posiblemente merezca ese calificativo desde el punto de vista académico; pero, ¿sabes qué te digo?, me funciona a la perfección. Te paso el código para que veas si puede servirte al menos la idea: Código: ' Hago recuento de registros que voy a traspasar para la posterior comprobacón
With rsTemp
If .State Then .Close
.Open "SELECT Fecha From " & cFiTur & _
" WHERE NúmPers =" & nOri & _
" And Fecha <= #" & Format(dFin, "mm-dd-yyyy") & "#", _
dbDataOp, adOpenStatic, adLockOptimistic
nReg = .RecordCount
.Close
End With
' Paso datos de Origen al temporal
SQL = "SELECT " & cFiTur & ".*" & _
" INTO Turnos IN 'c:\WinTurnosEnfer\Temporales.mdb'" & _
" From " & cFiTur & _
" WHERE ((" & cFiTur & ".NúmPers)=" & nOri & ")" & _
" And (" & cFiTur & ".Fecha) <= #" & Format(dFin, "mm-dd-yyyy") & "#);"
dbDataOp.Execute SQL
' Espero hasta que haya terminado de ejecutarse la consulta
'---------------------------------------------------------------------
With rsTemp
Comprobar:
If .State Then .Close
.Open "Select * From Turnos", dbTemOp, adOpenStatic, adLockOptimistic
If .RecordCount < nReg Then DoEvents: GoTo Comprobar
.Close
End With Creo que está claro que (cFiTur) es una variable que lleva el nombre del fichero de origen, y Turnos es el fichero temporal de destino de los datos.
Ojalá te den una solución mejor, pero si no, prueba a ver si de tanto insistir con mi error he llegado darte una salida válida. | 
10-07-2006, 08:27:32
| | Gran Participación en el Foro | | Registrado: may 2006 Posts: 108
| | Hola José María, perdón por la tardanza en contestar, he tenido unos días infernales y no he podido pasarme. Gracias por la idea, es una interesante opción para controlar cuándo un INSERT ha terminado.
¿Sabes lo peor de todo?, que al final, después de tanto quebradero de cabeza, la funcionalidad que necesitaba de esta secuencialización no va a ser implementada en la versión final porque han decidido hacer algunos cambios, así que os he vuelto locos innecesariamente.
En cualquier caso, muchas gracias por las aportaciones, seguro que me vendrán bien en un futuro.
Saludos | | Herramientas | | | | Desplegado | Mode Lineal |
Normas de Publicación
| no Puedes crear nuevos temas no Puedes responder a temas no Puedes adjuntar archivos no Puedes editar tus mensajes Código [IMG] está habilitado Código HTML está deshabilitado | | | La franja horaria es GMT. Ahora son las 08:43:50.
Powered by vBulletin® Version 3.6.8 Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO 3.1.0
A vBSkinworks Design
|  |