SQL疑难问题

最近,遇到并解决一个SQL上的疑难问题。考勤系统,记录着员工进出公司的刷卡记录。而员工刷卡并不规范,存在刷多次的情况。例如:出去时连续刷多次,进来时也连续刷多次。筛选有效刷卡记录数据的规则:对于出去时连续刷多次(包含一次)的情况,取第一次刷卡记录;对于进来时连续刷多次(包含一次)的情况,取最后一次的刷卡记录。考勤系统的数据量很大,假设公司有2万名员工,一员工一天100条刷卡记录。

用什么方法可以高效地查出某一时间范围内员工的有效刷卡记录?

测试表及测试数据如下:

create table Attendance
(
UserId  int,            --员工ID
ClockInTime datetime,   --员工刷卡时间
Flag char(1)            --进出标志 ‘1‘代表出,‘0‘代表进
) 

insert Attendance
values(100001,‘2015-06-01 08:03:00‘,‘1‘),
      (100001,‘2015-06-01 08:03:10‘,‘1‘),
      (100001,‘2015-06-01 08:03:50‘,‘1‘),
      (100001,‘2015-06-01 08:04:00‘,‘1‘),
      (100001,‘2015-06-01 08:10:00‘,‘0‘),
      (100001,‘2015-06-01 08:10:10‘,‘0‘),
      (100001,‘2015-06-01 08:15:00‘,‘1‘),
      (100001,‘2015-06-01 08:30:00‘,‘1‘),
      (100001,‘2015-06-01 08:40:10‘,‘0‘),
      (100001,‘2015-06-01 09:00:00‘,‘1‘),
      (100001,‘2015-06-01 09:15:10‘,‘0‘),
      (100001,‘2015-06-01 09:30:00‘,‘1‘),
      (100002,‘2015-06-01 08:03:00‘,‘0‘),
      (100002,‘2015-06-01 08:03:10‘,‘0‘),
      (100002,‘2015-06-01 08:03:50‘,‘1‘),
      (100002,‘2015-06-01 08:04:00‘,‘1‘),
      (100002,‘2015-06-01 08:10:00‘,‘1‘),
      (100002,‘2015-06-01 08:10:10‘,‘0‘),
      (100002,‘2015-06-01 08:15:00‘,‘1‘),
      (100002,‘2015-06-01 08:30:00‘,‘1‘),
      (100002,‘2015-06-01 08:40:10‘,‘1‘),
      (100002,‘2015-06-01 09:00:00‘,‘0‘),
      (100002,‘2015-06-01 09:15:10‘,‘0‘),
      (100002,‘2015-06-01 09:30:00‘,‘1‘)

而需筛选的有效数据为红色标记部分:

而作为测试数据,也就只提供两名员工,每人一天12条的刷卡记录,需要完成的是将红色标记的数据筛选出来。

不难看出问题的难点在于判断哪些数据是连续(进或出)的,无论出去还是进来。把这一点解决了,所有的问题也就迎刃而解。

1)首先,想到了递归查询。但是很快否定了想法,这个方法判断不出来数据是否连续。

2)其次,又考虑游标。或许游标能判断是否连续的问题,但是处理大数据量时,性能肯定极其低。

最后,闪现一个思路,没想到顺着这个思路把问题解决了。

1,先按UserID、日期分组,组内按ClockInTime升序排列。

select *,
ROW_NUMBER() over(partition by UserId,convert(varchar(10),ClockInTime,23) order by ClockInTime) as RN into #tmp
 from Attendance 

select * from #tmp order by UserId,ClockInTime

结果如图:

2,再按UserID、日期、Flag分组,组内按ClockInTime升序排列。

select *,
ROW_NUMBER() over(partition by UserId,convert(varchar(10),ClockInTime),Flag order by ClockInTime) as RN1 into #tmp1
from Attendance 

select * from #tmp1 order by UserId,ClockInTime

结果如图:

3,用#tmp1中的RN1与#tmp中的RN做差。

select a.*,b.RN1,b.RN1-a.RN as RN2 into #tmp2
 from #tmp as a,#tmp1 as b
 where a.UserId=b.UserId and a.ClockInTime=b.ClockInTime and a.Flag=b.Flag

select * from #tmp2 order by UserId,ClockInTime

结果如图:

3,根据UserID、日期、Flag、RN2可以判断出哪些数据是连续的,然后,用Row_Number()排序一下,就可以筛选所需要的数据。

select *,
 case when Flag=‘1‘ then ROW_NUMBER() over(Partition by UserID,convert(varchar(10),ClockInTime,23),Flag,RN2 order by ClockInTime)
 else ROW_NUMBER() over(Partition by UserID,convert(varchar(10),ClockInTime,23),Flag,RN2 order by ClockInTime desc) end as RId
 into #tmp3
 from #tmp2 

select * from #tmp3 order by UserId,ClockInTime

结果如图:

4,RID=‘1’的数据是正确的结果,即有效的刷卡记录数据。

select UserId,ClockInTime,Flag
from #tmp3
where Rid=‘1‘
order by UserId,ClockInTime

结果如图:

这样问题就解决了。进一步优化sql,其实1,2,3等3个步骤只要一步就解决了

select *,
ROW_NUMBER() over(partition by UserId,convert(varchar(10),ClockInTime,23) order by ClockInTime)-ROW_NUMBER() over(partition by UserId,convert(varchar(10),ClockInTime),Flag order by ClockInTime) as RN
from Attendance order by UserId,ClockInTime

有了上面查询的结果,后面的查询也简单多了。如果用一句SQL来解决的话,那就是:

select UserId,ClockInTime,Flag from (
select *,
case when Flag=‘1‘ then ROW_NUMBER() over(Partition by UserID,convert(varchar(10),ClockInTime,23),Flag,RN order by ClockInTime)
else ROW_NUMBER() over(Partition by UserID,convert(varchar(10),ClockInTime,23),Flag,RN order by ClockInTime desc) end as RId
from (
select *,
ROW_NUMBER() over(partition by UserId,convert(varchar(10),ClockInTime,23) order by ClockInTime)-ROW_NUMBER() over(partition by UserId,convert(varchar(10),ClockInTime),Flag order by ClockInTime) as RN
from Attendance
) as a
) as b
where RId=‘1‘ order by UserId,ClockInTime

  

时间: 2024-10-27 17:30:17

SQL疑难问题的相关文章

Oracle 常用系统函数

2  字符函数 1.    replace( 字符串1,字符串2,字符串3) replace( char, search_string, replace_string) 功能:在"字符串1"中搜索"字符串2",并将其替换为"字符串3". 例如下面的命令是将所有员工名字中出现的"A"替换为"中国". SQL>selectreplace(ename, 'A', '中国') from scott.emp;

sql语句聚合等疑难问题收集

数据表中比如有20条数据读取前5条数据select top 5 * from 表 order by id desc读取前5条数据后的5条数据(就是查询前10条记录,取后面5个)select top 5 * from 表 where id not in (select top 5 id from 表) ===================================================================================SELECT TOP 11 *

问题,SQL Server存储过...[疑难问题]

2bja1w派韶乱猎挤悠http://blog.sina.com.cn/s/blog_17d9ab3350102xsls.htmlu35gxb涣摆唾嚼概狡http://blog.sina.com.cn/s/blog_17da0697d0102y1am.htmlst7s9d堵套啡丛何孔http://blog.sina.com.cn/s/blog_17da068f80102ycte.html741j4r诒胃拷咀崭美http://blog.sina.com.cn/s/blog_17d9ab3a0010

SQL Server setup media does not support the language of the OS or does not have ENU localized files.

win7 64的安装sqlserver2008时点击setup.exe安装时就报SQL Server setup media does not support the language of the OS or does not have ENU localized files.试验了网上的几种方法(更改语言或用虚拟光驱加载然后打开)都对我没有任何用处,一筹莫展.最后无意间在setup.exe文件上右键弹出"兼容性疑难解答",查找解决方案,然后按照他的操作,操作完成,解决了.

第四篇 SQL Server代理配置数据库邮件

本篇文章是SQL Server代理系列的第四篇,详细内容请参考原文. 正如这一系列的前几篇所述,SQL Server代理作业是由一系列的作业步骤组成,每个步骤由一个独立的类型去执行.SQL Server代理同样提供创建警报,能够以通知的形式将消息发送给设定的操作员.这些通知可以通过数据库邮件发送,数据库邮件是内置在SQL Server和SQL Server代理能够发送和接收电子邮件.在这一篇,你将学习如何配置数据库邮件发送和接收操作,以及一些基本的数据库邮件故障排除步骤.数据库邮件的简要概述在S

SQL事务日志备份时的问题

1.在进行事务日志备份的时候,如下图: 3041 消息的疑难解答时的考虑事项:不会只是一个数据库或所有数据库出现问题吗?是备份到本地存储区或远程存储吗?哪种类型的备份 (数据库备份. 日志备份和差异备份) 是否出现故障?正在执行备份的应用程序 (SQL Server 代理作业. SQL 维护计划或 VDI/VSS 备份软件供应商提供的备份代理程序) 是什么?用于备份命令的选项是什么?什么是在其下运行 SQL Server 的帐户,此帐户没有所需的访问目标位置写入文件? 参考:http://sup

卸载sql server 2012

好不容易装上了sql server 2012数据库,可是却不能连接本地的数据库,后来发现缺少一些服务,于是决定重新安装,但是卸载却很麻烦,如果卸载不干净的话,重新安装会出问题,所以下面就总结一些方法: 在卸载sql server 2012后,大家都希望能够将注册表信息完全删干净,下面就将教您彻底删除sql server 2012注册表的方法,供您参考,删除之前,请一定要做好备份工作哟. 在卸载sql server 2012开始——运行:输入regedit 进入注册表编辑器,进入之后执行下列操作:

Oracle、pl/sql安装文档

oracle10g.pl/sql安装文档 Oracle在各种管理系统项目中是不可或缺到,pl/sql也是非常好用的图形化管理工具.初学者(我自己就是证明了)经常在这两个工具上消磨宝贵到时间.趁今天有闲,整理下oracle和plsql到安装方式(后附oracle和plsql到安装包),以备后用及方便他人.个人笔记本是win7 64位,实测win8可用. 下载并解压oracle安装包如下图: 右键单击setup.exe,在菜单中选择兼容性疑难解答,弹出新窗口. 选择尝试建议到设置,在跳转到页面中启动

【转】微软MVP攻略 (如何成为MVP?一个SQL Server MVP的经验之谈)

一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 初衷 什么是微软MVP? 成为微软MVP的条件? 如何成为微软MVP? (一) 申请时间划分 (二) 前期准备 (三) 下载/填写申请表格 (四) 申请MVP (五) 各种资料的填写 微软MVP奖项及权益包括什么? 成为微软MVP之后 个人建议 互动资讯 二.初衷 搞微软技术的,大家或多或少都有听说过微软的“最有价值专家”(MVP),网上也有不少资料对这个称谓做了介绍,但是都是一些大体的描述,并没有更加细节方面的,比