Corrección posible en recordsets

Nuevamente Ángeles.
Perdona que vuelva a molestar, no se si recuerdas la pregunta que te hice no hace mucho acerca de marcar unos valores "Entrada" o "Salida" en una tabla...
Me resolviste el problema excelentemente.
El caso es que se me ha ocurrido (no se si acertadamente o no) dejar la tabla en cuestión algo más pequeña. Verás, antes tenía los campos ID, CBARRAS, FECHA, HORA y TIPO (no recuerdo así a bote pronto si fueron esos los nombres finales con los que trabajamos en aquella ocasión). Si no me equivoco, puedo dejar más "liviana" la tabla quitando el campo FECHA o HORA, puesto que ambos guardan lo mismo, con diferente formato, y sería algo redundante tener los dos.
Pero el problema, y esa es mi primera consulta, es que en el código que me pasaste se hacían recordsets con ambos campos, y no se si se puede cambiar o no.
No es nada imprescindible, si es muy dificultoso o laborioso ni te molestes, pero yo tenía que consultarte...
La segunda parte de mi pregunta es acerca del tratamiento posterior de esos datos. Te explico.
Tengo actualmente una consulta sql sobre la tabla de marcajes, y otra sql sobre la primera consulta. Ya sabes, anidadas, alias... Me funciona bien, pero no se si he hecho lo más óptimo. Me gustaría que me dieras tu opinión o consejo sobre realizar un posible tratamiento de esos datos (más que nada, encontrar las diferencias de tiempos entre entradas y salidas en registros distintos, por cada empleado).
Actualmente encuentro los tiempos por días y empleados con las dos consultas, o sea, proceso los datos después de haberlos almacenado.
Bueno, más o menos es eso.

1 Respuesta

Respuesta
1
Respecto de lo de la fecha, sinceramente estuve por decirte que para que querías 2 campos si con 1 era suficiente. Lo que tendrás que hacer es suprimir el rs2, el que busca la hora, quedaría más o menos así:
Private Sub CBarras_LostFocus()
Dim db As Database
Dim rs As Recordset
Dim rs1 As Recordset
Dim rs3 As Recordset
Set db = CurrentDb
Set rs = db.OpenRecordset("Select cBarras from T_Picadas where cBarras='" & Form!CBarras.Value & "'")
If rs.EOF = True Then
Form!Tipo.Value = "Entrada"
Else
    Set rs1 = db.OpenRecordset("Select Max(Fecha) as MaxFecha From T_Picadas where cBarras='" & Form!CBarras.Value & "'")
    Set rs3 = db.OpenRecordset("Select Tipo From T_Picadas where Fecha=cdate('" & rs1!MaxFecha & "')  and cBarras='" & Form!CBarras.Value & "'")
    If rs3!Tipo = "Entrada" Then
        Form!Tipo.Value = "Salida"
    Else
        Form!Tipo.Value = "Entrada"
    End If
End If
End Sub
Creo que así funcionaria, pruébalo y me cuentas.
Respecto de la otra pregunta... ¿puedes poner un ejemplo que lo que quieres?
1º Con respecto al código modificado.
No quise tocar nada ni quitar el rs2 (ni tocar el rs3) porque no pillo aún mucho de recordsets y no sabía si evaluabas la hora en las sql que llevan. Me imagino, corrígeme si me equivoco, que cuando evalúas Fecha=cdate(... ya se toman los "valores" de fecha y hora. (Lo del cdate tengo que estudiármelo).
No lo he probado aún, pero esta noche me pongo a ello.
2º Con respecto a la otra pregunta, que reconozco que no he sido muy claro explicándome.
Con tu código almaceno los datos más o menos así:
Id - cbarras - fecha - tipo
---------------------------------------
1 - 2 - 19/11/2010 7:24 - E
2 - 1 - 19/11/2010 7:25 - E
3 - 2 - 19/11/2010 12:31 - S
4 - 3 - 19/11/2010 12:35 - E
5 - 1 - 19/11/2010 14:14 - S
6 - 2 - 19/11/2010 15:02 - E
7 - 3 - 19/11/2010 17:45 - S
8 - 2 - 19/11/2010 19:55 - S
9 - 2 - 20/11/2010 7:13 - E
10 - 2 - 20/11/2010 9:25 - S
...
Y he podido "componer" esto, a modo "no soy programador", con ayuda, obviamente:
SELECT T_PICADAS.CBARRAS, DateValue(T_PICADAS.FECHA) AS RptDia, T_PICADAS.FECHA AS ENTRADA, (SELECT TOP 1 Out.FECHA
FROM T_PICADAS AS Out
WHERE Out.CBARRAS= T_PICADAS.CBARRAS
AND DateValue(T_PICADAS.FECHA) = DateValue(Out.FECHA)
AND T_PICADAS.FECHA < Out.FECHA
AND Out.TIPO = "S"
ORDER BY Out.FECHA ASC, Out.CBARRAS) AS SALIDA
FROM T_PICADAS
WHERE T_PICADAS.TIPO = "E";
Y luego esta:
SELECT query1.RptDia, query1.CBARRAS, Sum(DateDiff("n",[ENTRADA],[SALIDA])) AS DifMinutos, Format(Sum(DateDiff("n",[ENTRADA],[SALIDA]))/1440,"hh:nn") AS DifHoras
FROM query1
WHERE (((query1.SALIDA) Is Not Null))
GROUP BY query1.RptDia, query1.CBARRAS
ORDER BY query1.RptDia, query1.CBARRAS;
Y me queda lo que quería:
DIA - CBARRAS - MINUTOS - HORAS
--------------------------------------------
19/11/2010 - 2 - 600 - 10:00
19/11/2010 - 3 - 310 - 05:10
19/11/2010 - 1 - 409 - 06:49
20/11/2010 - 2 - 132 - 02:12
PERO, tiene fallos. Si un registro se llena a medias por algún error tengo que buscarlo manualmente o no me deja sacar la consulta. Si se meten 2 entradas de algún CBARRAS seguidas (sería señal de un fallo pero puede pasar) me da datos erróneos. Y la principal, ¿me da la sensación de que es algo muy... pesado? Contaré con unos 80.000 registros anuales, ¿será una consulta demasiado engorrosa para ese número de registros?
Algunas recomendaciones que me han llegado me han sugerido que plantee el tema de otra forma, algo como esto para almacenar:
ID - CBARRAS - ENTRADA - SALIDA
------------------------------------------
1 - 2 - 19/11/2010 7:24 - 19/11/2010 12:31
2 - 1 - 19/11/2010 7:25 - 19/11/2010 14:14
3 - 3 - 19/11/2010 12:35 - 19/11/2010 17:45
4 - 2 - 19/11/2010 15:02 - 19/11/2010 19:55
5 - 2 - 20/11/2010 7:13 - 20/11/2010 9:25
...
Así tengo menos registros, la mitad, pero me obliga a tratar datos (buscar quién tiene ya su entrada y quién no) en los propios marcajes, o sea, a la hora de entrar datos, y no sabría hacerlo. Si, luego una simple consulta me daría las diferencias de tiempos y tal, pero Ángeles, me gustaría conocer al menos tu opinión acerca del tema, ver si es mejor hacerlo de una forma u otra... no se, yo también ando algo perdido y me explico regular...
Y una última cuestión, si uso el primer sistema, el del origen de todo este cotarro, ¿el dato que yo me he empeñado en meter en la tabla, el del campo TIPO, "E" o "S", es necesario? ¿No es algo redundante teniendo en cuenta que el campo fecha es único y que ya existe también un campo ID?
No me he olvidado, es que me he cambiado de ordenador y cuando ya lo tenia todo más o menos controlado, tenia un fallo en la pantalla (salia una raya blanca), por lo que me lo han cambiado y he tenido que empezar a preparlo de nuevo, ya lo tengo casi todo. Te diré que el recordset no es más que una consulta de selección, es decir una vista de la tabla con las condiciones que quieras poner. La función cDate() lo que hace es devolver un tipo fecha de una cadena válida para fecha. Por ejemplo, si tú mediante un InputBox pides una fecha y el usuario teclea 26/11/2010 esto por si mismo es una cadena y no lo podrías igualar a un campo tipo fecha de la tabla (te saldría el famoso error "Nocoinciden los tipos de datos"), pero si pones cDate('26/11/2010') entonces si sería tipo fecha y lo podrías igualar.
Respecto al código, mañana lo miro y te cuento.
Estoy con tú pregunta y lo que creo es que esto no se debe de hacer por consulta, es demasiado lento, si lo que he entendido es correcto lo que quieres son las horas y minutos trabajados por cBarras por día. Yo lo que plantearía sería calcular esas horas y minutos y pasarlos a otra tabla de forma que te quedara:
Fecha cBarras Horas Minutos
Una vez calculado, los registros de la tabla llemémosla Fichajes los pasaría a un HistoricoFichajes y los borraría de Fichajes. Lo de pasarlo a un histórico es para no perder los datos y en cualquier momento poder recuperarlos y además aligerarías, enormemente la tabla Fichajes.
En cuanto a tener 2 entradas o 2 salidas seguidas, no lo creo posible, ya que el código busca el último registro y si es entrada pone salida o viceversa, de esta forma aunque la entrada y la salida se produzcan en el mismo minuto nunca habría 2 seguidas.
Piénsalo y si te parece bien, me pongo con el código.
Ángeles... me estás dejando en un punto en el que no sabría cómo agradecerte tu atención, vamos, que me está sobrepasando...
Yo, modestamente, pretendía hacer un pequeño "apartado", dentro de la bd que tenemos para gestión interna, con el que llevar el control de asistencia. Y... No aspiraba a dejar hecha una cosa demasiado profesional, entre otras cosas porque yo no me dedico a esto, es más bien aquello de "bueno, lo miro, algo sencillito no será muy difícil de hacer...". Claro, cuando me he puesto a ello, he ido leyendo, aprendiendo algunas cosillas y siempre que termino algo me parece mejorable...(creo que eso es porque no planteo las cosas como programador, obvio, si es que no lo soy).
De ahí que lo que tengo hecho me planteara dudas y quisiera conocer tu opinión.
Y ahora leyéndola... pues es una forma de afrontar el problema en la que yo no hubiera caído en la vida, y, francamente, me gusta.
Mi principal duda era con respecto a usar o no una consulta, porque me daba la impresión de ser... ya te dije, algo "fullero". Tu comentario me confirma esa sospecha mía, y tu solución evita la consulta (aporta una nueva solución más factible y profesional) y me aporta una forma de guardar los datos de una manera más óptima, porque si no te he entendido mal, de lo que me hablas es de usar la tabla de fichajes que tengo actualmente como "transitoria" por llamarlo de alguna forma, y de ahí procesar los datos con sus cálculos correspondientes y guardarlos definitivamente en OTRA tabla, a modo de histórico.
Buah, eso hecho así yo no sabría hacerlo, pero me dejaría el control de asistencia realmente fantástico, pues la entrada de datos no es lo que me preocupaba más, sino la correcta utilización y creación de informes después. Y crear informes sobre una tabla con los registros ya calculados SI que es algo que sabré hacer.
Pero es que me da ya hasta apuro, yo esperaba un poco de ayuda y esto es ya demasiado Ángeles... no sé cómo agradecer tu trabajo...
PD (3 puntualizaciones):
1ª No es que necesite minutos y horas, tal como sale en la consulta que puse, eso era porque en el proceso de probar y probar me salió primero la columna de minutos y después la utilicé para convertirla al formato que necesitaba, y lo dejé así, pero no era necesario.
2ª Lo de las 2 entradas o salidas juntas, sabiendo lo que me comentas, deja de preocuparme, tienes razón.
3ª Tus explicaciones en la primera respuesta (además de tu comentario de la segunda respuesta) son dignas de agradecer, porque me has explicado cosas que HE APRENDIDO y eso no tiene precio. Yo me dedico a la docencia y en eso si que sé de lo que hablo.
Y una 4ª, que se me acaba de venir a la cabeza: te dejo mi mail por si estimas conveniente usarlo
jdam7799(arroba)hotmail(punto)com
He agregado un campo nuevo, Observaciones, a la tabla t_picadas para controlar si hay 2 entradas seguidas o 2 salidas seguidas.
Las otras tablas se llaman: TiempoTrabajado (cBarras, Fecha, Horas, Minutos) e HistoricoFichajes, con los mismos campos que t_picadas menos el de las observaciones.
Te mando el código, más o menos explicado, cualquier consulta, ya sabes donde estoy.
Function CalculoTiempo()
'Declaro las variables
Dim db As Database
Dim rs As Recordset
Dim rs1 As Recordset
Dim rs2 As Recordset
Dim vMinutos As Double
Dim vHoras As Double
Dim vHora As Date
Dim vFichaje As String
'Activo la Base de Datos
Set db = CurrentDb
'Creo el 1er recordset abriendo la tabla TiempoTrabajado en la que tenemos que insertar
Set rs = db.OpenRecordset("TiempoTrabajado")
'Creo el 2º recordset, en este lo que hago, por medio de group by, es tener un solo registro por cBarras y Fecha
Set rs1 = db.OpenRecordset("Select cBarras, Fecha From T_PICADAS group by cBarras, Fecha")
'Recorro este recordset por medio de do While...Loop
'Mientras no sea final de fichero (EOF ---> Final de fichero)
Do While Not rs1.EOF
'Creo el 3er recordset seleccionando de la tabla T_PICADAS los registro de un cBarras en una fecha (dado por el 2º recordset)
    Set rs2 = db.OpenRecordset("Select * From T_PICADAS where cBarras='" & rs1!cBarras & "' and Fecha=cDate('" & rs1!Fecha & "') order by Hora")
'Inicializo las variables vMinutos (vamos a meter la diferencia de minutos entre entrada y salida) y vFichaje (para controlar si hay 2 entradas o 2 salidas seguidas)
    vMinutos = 0
    vFichaje = ""
'Recorro el recordset
    Do While Not rs2.EOF
'El 1er registro de cada bloque tendrá de ser de entrada, sino sería erroneo
    If rs2!tipo = "E" Then
'Si es entrada "E", inicializo la variable vHora con la hora del registro y paso al siguiente registro
        vHora = rs2!Hora
        rs2.MoveNext
        vFichaje = "Correcto"
    Else
'Si el fichaje es incorrecto actualizo el campo Observaciones y salgo del bucle
'Para este cBarras y Fecha no hago nada más
        DoCmd.RunSQL "Update T_PICADAS Set Observaciones='Fichaje Incorrecto' where cBarras='" & rs1!cBarras & "' and Fecha=cDate('" & rs1!Fecha & "')"
        vFichaje = "Incorrecto"
        Exit Do
    End If
'En este registro actuaré de igual forma que el anterior con la diferencia que tendrá que ser de salida "S"
    If rs2!tipo = "S" Then
'Como puede haber varios fichajes en el día, la variable vMinutos lo vamos acumulando
'DateDiff ---> Función que calcula la diferencia entre dos fecha/hora "n" ---> Diferencia en minutos
        vMinutos = vMinutos + DateDiff("n", vHora, rs2!Hora)
        rs2.MoveNext
        vFichaje = "Correcto"
    Else
        DoCmd.RunSQL "Update T_PICADAS Set Observaciones='Fichaje Incorrecto' where cBarras='" & rs1!cBarras & "' and Fecha=cDate('" & rs1!Fecha & "')"
        vFichaje = "Incorrecto"
        Exit Do
    End If
    Loop
    If vFichaje = "Correcto" Then
'Si es correcto se añade un nuevo registro (en blanco) al recodset rs, es decir a la tabla TiempoTrabajado
'Se igualan los campos para después actualizarlos.
'El campo Horas será el entero de dividir los minutos entre 60 (Función Int())
'El campo Minutos será el resto de dividir los minutos entre 60 (Función MOD)
        rs.AddNew
        rs!cBarras = rs1!cBarras
        rs!Fecha = rs1!Fecha
        rs!Horas = Int(vMinutos / 60)
        rs!Minutos = vMinutos Mod 60
        rs.Update
'Por último paso a histórico y borro de T_PICADAS
        DoCmd.RunSQL "Insert Into HistoricoFichajes (cBarras, Fecha, Hora, Tipo) Select cBarras, Fecha, Hora, Tipo From T_PICADAS where cBarras='" & rs1!cBarras & "' and Fecha=cDate('" & rs1!Fecha & "')"
        DoCmd.RunSQL "Delete * From T_PICADAS where cBarras='" & rs1!cBarras & "' and Fecha=cDate('" & rs1!Fecha & "')"
    End If
'Voy al siguiente código-fecha
rs1.MoveNext
Loop
End Function
No me acordaba que tenia tú correo, te he enviado la BD que hice para probar. El código lo tienes en la pestaña de módulos: modulo1.

Añade tu respuesta

Haz clic para o

Más respuestas relacionadas