SQL根据给定条件安排计算时间

时间:2014-07-29 11:56:58

标签: tsql sql-server-2005

我有以下架构和数据:

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[EntryExitLogs]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[EntryExitLogs](
    [DeviceLogId] [int] NOT NULL,
    [EmployeeCode] [nvarchar](50) NOT NULL,
    [LogDate] [datetime] NOT NULL,
    [Direction] [nvarchar](255) NOT NULL,
 CONSTRAINT [PK_EntryExitLogs] PRIMARY KEY CLUSTERED 
(
    [EmployeeCode] ASC,
    [LogDate] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
END
GO

INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('435859','30032','2014-01-21 07:04:41','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('438019','30032','2014-01-21 08:59:09','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('441564','30032','2014-01-21 16:57:35','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('441263','30032','2014-01-21 19:09:19','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('441264','30032','2014-01-21 19:10:20','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('439928','34035','2014-01-21 08:29:59','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('437962','34035','2014-01-21 08:30:12','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('437992','34035','2014-01-21 08:47:33','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('440203','34035','2014-01-21 13:38:56','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('442858','34035','2014-01-21 16:34:08','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('442860','34035','2014-01-21 16:35:11','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('441283','34035','2014-01-21 19:16:58','out');

我已经编写了SQL来计算这样的进出时间:

;WITH cte AS (
                 SELECT ROW_NUMBER() OVER(
                            PARTITION BY lt.EmployeeCode ORDER BY lt.EmployeeCode,
                            lt.LogDate
                        )              AS RowNo,
                        lt.DeviceLogId,
                        lt.EmployeeCode,
                        lt.LogDate,
                        lt.Direction
                 FROM   EntryExitLogs     lt
             )
SELECT i.EmployeeCode,
       i.LogDate AS InTime,
       (
           SELECT MIN(o.LogDate)
           FROM   cte AS o
           WHERE  o.EmployeeCode = i.EmployeeCode
                  AND o.RowNo = (i.RowNo + 1)
                  AND o.Direction = 'out'
       )    AS OutTime
FROM   cte  AS i
WHERE  i.Direction = 'in'
ORDER BY i.EmployeeCode, i.LogDate

我正在获取输出(但不是我所希望的),但我希望以下列方式输出(注释是针对每一行提供的更多信息):

EmployeeCode InTime             OutTime                 Comments
30032   21-Jan-2014 07:04:41    21-Jan-2014 08:59:09    
30032   21-Jan-2014 16:57:35    21-Jan-2014 19:09:19    
30032   21-Jan-2014 19:10:20    NULL                    If no OUT is specified for the last IN then it should be NULL
34035   21-Jan-2014 08:29:59    21-Jan-2014 13:38:56    Earliest IN and Latest OUT to be taken in case of multiple IN & OUT
34035   21-Jan-2014 16:34:08    21-Jan-2014 19:16:58    Earliest IN and Latest OUT to be taken in case of multiple IN & OUT

请找到此here

的架构

请帮助我实现这一目标。

1 个答案:

答案 0 :(得分:1)

这应该有效:

;WITH TrueOut AS --select only the latest "out" in between two "ins"
(
SELECT *
FROM EntryExitLogs a 
WHERE direction='out'
AND 
ISNULL((SELECT MIN(LogDate) FROM EntryExitLogs b 
WHERE a.EmployeeCode=b.Employeecode
AND b.direction='out'
AND b.LogDate>a.LogDate),'9999-12-31')
>=
ISNULL((SELECT MIN(LogDate) FROM EntryExitLogs b 
WHERE a.EmployeeCode=b.Employeecode
AND b.direction='in'
AND b.LogDate>a.LogDate),'9999-12-31')
),
TrueIn AS --select only the earlies "in" in between two "outs"
(
SELECT *
FROM EntryExitLogs a 
WHERE direction='in'
AND 
ISNULL((SELECT MAX(LogDate) FROM EntryExitLogs b 
WHERE a.EmployeeCode=b.Employeecode
AND b.direction='out'
AND b.LogDate<a.LogDate),'1900-01-01')
>=
ISNULL((SELECT MAX(LogDate) FROM EntryExitLogs b 
WHERE a.EmployeeCode=b.Employeecode
AND b.direction='in'
AND b.LogDate<a.LogDate),'1900-01-01')
)
-- For every in select the next out
SELECT a.EmployeeCode, a.LogDate InTime, 
(SELECT MIN(LogDate) 
FROM TrueOut b
WHERE a.EmployeeCode=b.EmployeeCode
AND a.LogDate<b.LogDate) OutTIme
FROM TrueIn a