Principales respuestas
Evitar Marcacion Doble en asistencia de personal

Pregunta
-
Buen día, en la empresa donde estoy laborando tienen un marcador de huella digital de marca "zkteco k40", el cual manda los registros directamente a una tabla en sql, muchas veces un empleados marca dos veces casi al mismo tiempo, como en la imagen que muestro a continuación.
Quería saber de que forma puedo evitar esos registros que tienen muy poco margen de tiempo entre uno y otro.
Respuestas
-
Hola Abram N. Cueva:
Dudo mucho que puedas evitarlo, pero aunque pudieses, no es más fácil, ya los registros si se hicieron porque el usuario los hizo, no mostrarlos en la vista, que utilices.
create table tiemposdif (userid int, checktime datetime) go insert into tiemposdif (userid, checktime) values (1,'20180905 22:23:38.000'), (1,'20180905 22:23:49.000'), (2,'20180910 00:23:39.000'), (2,'20180910 01:55:42.000'), (3,'20180910 02:23:45.000'), (3,'20180910 02:23:50.000') go ;with cte as (select userid, checktime from tiemposdif ) select c.* from cte c left join cte d on c.userid = d.userid and d.checktime >= c.checktime and d.checktime< DATEADD(second,20,c.checktime) where d.userid is null
Resultado
Algo del estilo.
Un saludo
- Marcado como respuesta Pablo RubioModerator miércoles, 12 de septiembre de 2018 16:49
-
Aca otra sugerencia para no tomar en cuenta esas filas.
DECLARE @tiemposdif table ( userid int NOT NULL, checktime datetime NOT NULL ); insert into @tiemposdif (userid, checktime) VALUES (1,'20180905 22:23:38.000'), (1,'20180905 22:23:49.000'), (2,'20180910 00:23:39.000'), (2,'20180910 01:55:42.000'), (3,'20180910 02:23:45.000'), (3,'20180910 02:23:50.000'); DECLARE @seconds int = 20; WITH R AS ( SELECT *, CASE WHEN DATEDIFF(SECOND, LAG(checktime) OVER(PARTITION BY userid ORDER BY checktime), checktime) < @seconds THEN 1 ELSE 0 END lt_3s_flag FROM @tiemposdif ) SELECT * FROM R WHERE lt_3s_flag = 0 ; GO
Si adicionas un indice por (userid, checktime) evitaras el operador de ordenar (sort operator) en el plan de ejecucion.
AMB
Some guidelines for posting questions...
AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas- Editado HunchbackMVP jueves, 6 de septiembre de 2018 15:11
- Marcado como respuesta Pablo RubioModerator miércoles, 12 de septiembre de 2018 16:49
-
En caso de insistir en no permitir esas filas donde la diferencia en segundos entre esta y la immediata anterior para el mismo usuario sea menor a 20 u otro numero de referencia, entonces pudieras jugar con crear una funcion escalar y usar esta en una restriccion de chequeo.
CREATE table dbo.tiemposdif ( sk int NOT NULL IDENTITY PRIMARY KEY, userid int NOT NULL, checktime datetime NOT NULL, UNIQUE NONCLUSTERED (userid, checktime) ) GO CREATE FUNCTION dbo.ufn_myfn ( @userid int, @checktime datetime ) RETURNS int AS BEGIN DECLARE @diff int = NULL; WITH R AS ( SELECT TOP (1) checktime FROM dbo.tiemposdif WHERE userid = @userid AND checktime < @checktime ORDER BY checktime DESC ) SELECT @diff = DATEDIFF(SECOND, R.checktime, @checktime) FROM R; RETURN @diff; END GO ALTER TABLE dbo.tiemposdif ADD CONSTRAINT check_diff_gt_seconds CHECK (ISNULL(dbo.ufn_myfn(userid, checktime), 20) >= 20) GO -- ok insert into tiemposdif (userid, checktime) VALUES (1,'20180905 22:23:38.000'); GO -- will fail insert into tiemposdif (userid, checktime) VALUES (1,'20180905 22:23:49.000'); GO -- ok insert into tiemposdif (userid, checktime) VALUES (2,'20180910 00:23:39.000'), (2,'20180910 01:55:42.000'), (3,'20180910 02:23:45.000'); GO -- will fail insert into tiemposdif (userid, checktime) VALUES (3,'20180910 02:23:50.000'); GO SELECT * FROM dbo.tiemposdif GO DROP TABLE dbo.tiemposdif GO DROP FUNCTION dbo.ufn_myfn GO
AMB
Some guidelines for posting questions...
AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas- Marcado como respuesta Pablo RubioModerator miércoles, 12 de septiembre de 2018 16:49
-
Abram,
Seria de mucha ayuda para nosotros que postearas la estructura de la tabla, data de ejemplo en forma de sentencias INSERT, asi como el resultado esperado. Tal como Javi lo hizo en su primera respuesta.
Ayudanos para poder ayudarte.
dicho lo anterior, es importante tambien saber con que version de SQL Server cuentas pues para este tipo de problemas usar funciones de off-set (LEAD / LAG) hacen el trabajo mas simple.
Primero debrias excluir las filas que segun tu criterio consideras duplicadas, luego con las filas restantes hacer la logica.
Un forma seria enumerar las filas por cada usiario y dia para que puedes identificar pares de filas y por lo tanto las filas impares serian entrada y las pares salida.
AMB
Some guidelines for posting questions...
AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas- Editado HunchbackMVP viernes, 7 de septiembre de 2018 12:08
- Marcado como respuesta Pablo RubioModerator miércoles, 12 de septiembre de 2018 16:49
-
Que te parece esta opción.
select ID_TRABAJADOR,cast(FechaAsistencia as date)Fecha,min(FechaAsistencia)Entrada, case when DATEDIFF ( Hour , min(FechaAsistencia) , max(FechaAsistencia) ) >= 1 /*Horas laborables*/ then CONVERT(varchar ,MAX(FechaAsistencia),20) else 'Sin Marcación de Salida ó Hora Por defecto de salida.' end Salida, DATEDIFF ( Hour , min(FechaAsistencia) , max(FechaAsistencia) ) HorasTrabajadas from Asistencia group by ID_TRABAJADOR,cast(FechaAsistencia as date)
Coméntame.
- Editado Blueh Scouth viernes, 7 de septiembre de 2018 22:09
- Marcado como respuesta Pablo RubioModerator miércoles, 12 de septiembre de 2018 16:49
Todas las respuestas
-
Hola Abram N. Cueva:
Dudo mucho que puedas evitarlo, pero aunque pudieses, no es más fácil, ya los registros si se hicieron porque el usuario los hizo, no mostrarlos en la vista, que utilices.
create table tiemposdif (userid int, checktime datetime) go insert into tiemposdif (userid, checktime) values (1,'20180905 22:23:38.000'), (1,'20180905 22:23:49.000'), (2,'20180910 00:23:39.000'), (2,'20180910 01:55:42.000'), (3,'20180910 02:23:45.000'), (3,'20180910 02:23:50.000') go ;with cte as (select userid, checktime from tiemposdif ) select c.* from cte c left join cte d on c.userid = d.userid and d.checktime >= c.checktime and d.checktime< DATEADD(second,20,c.checktime) where d.userid is null
Resultado
Algo del estilo.
Un saludo
- Marcado como respuesta Pablo RubioModerator miércoles, 12 de septiembre de 2018 16:49
-
Perdón, no es que dude mucho que puedas evitarlo, dudo mucho que debas de hacerlo.
Piensalo tranquilamente, porque esas pocas unidades de tiempo, que por cierto en el ejemplo son 10 segundos, dentro de un tiempo te pueden traer problemas.
Un saludo
-
Aca otra sugerencia para no tomar en cuenta esas filas.
DECLARE @tiemposdif table ( userid int NOT NULL, checktime datetime NOT NULL ); insert into @tiemposdif (userid, checktime) VALUES (1,'20180905 22:23:38.000'), (1,'20180905 22:23:49.000'), (2,'20180910 00:23:39.000'), (2,'20180910 01:55:42.000'), (3,'20180910 02:23:45.000'), (3,'20180910 02:23:50.000'); DECLARE @seconds int = 20; WITH R AS ( SELECT *, CASE WHEN DATEDIFF(SECOND, LAG(checktime) OVER(PARTITION BY userid ORDER BY checktime), checktime) < @seconds THEN 1 ELSE 0 END lt_3s_flag FROM @tiemposdif ) SELECT * FROM R WHERE lt_3s_flag = 0 ; GO
Si adicionas un indice por (userid, checktime) evitaras el operador de ordenar (sort operator) en el plan de ejecucion.
AMB
Some guidelines for posting questions...
AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas- Editado HunchbackMVP jueves, 6 de septiembre de 2018 15:11
- Marcado como respuesta Pablo RubioModerator miércoles, 12 de septiembre de 2018 16:49
-
En caso de insistir en no permitir esas filas donde la diferencia en segundos entre esta y la immediata anterior para el mismo usuario sea menor a 20 u otro numero de referencia, entonces pudieras jugar con crear una funcion escalar y usar esta en una restriccion de chequeo.
CREATE table dbo.tiemposdif ( sk int NOT NULL IDENTITY PRIMARY KEY, userid int NOT NULL, checktime datetime NOT NULL, UNIQUE NONCLUSTERED (userid, checktime) ) GO CREATE FUNCTION dbo.ufn_myfn ( @userid int, @checktime datetime ) RETURNS int AS BEGIN DECLARE @diff int = NULL; WITH R AS ( SELECT TOP (1) checktime FROM dbo.tiemposdif WHERE userid = @userid AND checktime < @checktime ORDER BY checktime DESC ) SELECT @diff = DATEDIFF(SECOND, R.checktime, @checktime) FROM R; RETURN @diff; END GO ALTER TABLE dbo.tiemposdif ADD CONSTRAINT check_diff_gt_seconds CHECK (ISNULL(dbo.ufn_myfn(userid, checktime), 20) >= 20) GO -- ok insert into tiemposdif (userid, checktime) VALUES (1,'20180905 22:23:38.000'); GO -- will fail insert into tiemposdif (userid, checktime) VALUES (1,'20180905 22:23:49.000'); GO -- ok insert into tiemposdif (userid, checktime) VALUES (2,'20180910 00:23:39.000'), (2,'20180910 01:55:42.000'), (3,'20180910 02:23:45.000'); GO -- will fail insert into tiemposdif (userid, checktime) VALUES (3,'20180910 02:23:50.000'); GO SELECT * FROM dbo.tiemposdif GO DROP TABLE dbo.tiemposdif GO DROP FUNCTION dbo.ufn_myfn GO
AMB
Some guidelines for posting questions...
AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas- Marcado como respuesta Pablo RubioModerator miércoles, 12 de septiembre de 2018 16:49
-
Agradezco la respuesta que me han brindado, me han servido a elaborar una consulta, que aun no esta finalizada, la cual me muestra los siguientes resultados.
Como ven en la imagen, en la segunda fila la fecha ha pasado mas de un día con respecto al registro anterior, por lo tanto ya no correspondería la salida, sino una nueva entrada, y eso conllevaría a cambiar tambien la columna "ENTRADA_SALIDA" de las restantes filas del mismo trabajador.
-
Abram,
Seria de mucha ayuda para nosotros que postearas la estructura de la tabla, data de ejemplo en forma de sentencias INSERT, asi como el resultado esperado. Tal como Javi lo hizo en su primera respuesta.
Ayudanos para poder ayudarte.
dicho lo anterior, es importante tambien saber con que version de SQL Server cuentas pues para este tipo de problemas usar funciones de off-set (LEAD / LAG) hacen el trabajo mas simple.
Primero debrias excluir las filas que segun tu criterio consideras duplicadas, luego con las filas restantes hacer la logica.
Un forma seria enumerar las filas por cada usiario y dia para que puedes identificar pares de filas y por lo tanto las filas impares serian entrada y las pares salida.
AMB
Some guidelines for posting questions...
AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas- Editado HunchbackMVP viernes, 7 de septiembre de 2018 12:08
- Marcado como respuesta Pablo RubioModerator miércoles, 12 de septiembre de 2018 16:49
-
Buen dia, estoy usando el sql server 2012 y la tabla que tengo es la siguiente:
create table Asistencia (ID_TRABAJADOR int, FechaAsistencia datetime)
go
insert into Asistencia (ID_TRABAJADOR, FechaAsistencia) values
(3, '20180906 21:55:08.000'),
(3, '20180906 21:55:44.000'),
(3, '20180906 21:56:32.000'),
(3, '20180907 22:21:24.000'),
(13,'20180906 21:55:28.000'),
(13,'20180906 21:56:24.000'),
(13,'20180906 22:00:59.000'),
(13,'20180906 22:05:40.000'),
(13,'20180906 22:19:24.000'),
(13,'20180906 22:21:03.000'),
(13,'20180906 22:21:54.000')
Go
-- Esta la consulta que muestra el siguiente resultado
SELECT Asistencia.Id_Trabajador AS ID_TRABAJADOR, FechaAsistencia AS FECHA,
row_number() OVER (PARTITION BY Asistencia.Id_Trabajador
ORDER BY (FechaAsistencia)) AS FILA, (CASE WHEN (row_number() OVER (PARTITION BY Asistencia.Id_Trabajador
ORDER BY (FechaAsistencia))) % 2 = '0' THEN 'SALIDA' ELSE 'ENTRADA' END) AS ENTRADA_SALIDA
FROM dbo.Asistencia
GROUP BY FechaAsistencia, Id_Trabajador
Como ven cada numeo impar es entrada e impar es salida, ahora lo que quiero cambiar es que cuando entre una entrada y salida haya una diferencia de 24 horas o mas la salida, sea considerada como entrada y por lo tanto sus demas filas tambien cambiarian, como se ve en la imagen mostrada a continuacion
-
Que te parece esta opción.
select ID_TRABAJADOR,cast(FechaAsistencia as date)Fecha,min(FechaAsistencia)Entrada, case when DATEDIFF ( Hour , min(FechaAsistencia) , max(FechaAsistencia) ) >= 1 /*Horas laborables*/ then CONVERT(varchar ,MAX(FechaAsistencia),20) else 'Sin Marcación de Salida ó Hora Por defecto de salida.' end Salida, DATEDIFF ( Hour , min(FechaAsistencia) , max(FechaAsistencia) ) HorasTrabajadas from Asistencia group by ID_TRABAJADOR,cast(FechaAsistencia as date)
Coméntame.
- Editado Blueh Scouth viernes, 7 de septiembre de 2018 22:09
- Marcado como respuesta Pablo RubioModerator miércoles, 12 de septiembre de 2018 16:49
-
-
-