将列选择为按列值分组的行

时间:2016-01-23 09:14:56

标签: sql sql-server-2008

我有一个登录表,如下所示:

登录表

Emp_ID    Created                |  Action
1       20/01/2016 10:44:42 AM      login
1       20/01/2016 4:45:49 PM       logout
1       20/01/2016 6:30:13 PM       logout
1       21/01/2016 8:46:28 AM       login
1       21/01/2016 9:46:42 AM       login
1       21/01/2016 1:46:46 PM       logout
1       22/01/2016 8:49:21 AM       login
1       22/01/2016 1:49:27 PM       logout
1       22/01/2016 2:29:53 PM       login
1       22/01/2016 2:30:13 PM       logout
3       22/01/2016 2:42:06 PM       login
1       22/01/2016 9:57:22 PM       login
1       22/01/2016 10:22:23 PM      logout
1       23/01/2016 8:01:47 AM       login
1       23/01/2016 9:01:58 AM       logout
3       23/01/2016 8:02:06 AM       login
3       23/01/2016 9:02:28 AM       logout

员工表

| ID | Fname | Lname |
|----|-------|-------|
|  1 | James | Brown |
|  2 |  Mark |  Bond |
|  3 |  Kemi |   Ojo |

我得到的结果

|    created |    login |   logout |    Employee | Emp_ID |
|------------|----------|----------|-------------|--------|
| 2016-01-20 | 10:44:42 | 18:30:13 | James Brown |      1 |
| 2016-01-21 | 08:46:28 | 13:46:46 | James Brown |      1 |
| 2016-01-22 | 08:49:21 | 22:22:23 | James Brown |      1 |
| 2016-01-22 | 14:42:06 | 22:22:23 |    Kemi Ojo |      3 |
| 2016-01-23 | 08:01:47 | 09:02:28 | James Brown |      1 |
| 2016-01-23 | 08:02:06 | 09:02:28 |    Kemi Ojo |      3 |

以下是我的尝试:

SELECT
    CAST(LI.created AS DATE) AS created,
    MIN(CAST(LI.created AS TIME)) AS login,
    MAX(CAST(LO.created AS TIME)) AS logout,
    e.fname+' '+e.lname Employee, li.Emp_ID

FROM
    Logins LI
LEFT OUTER JOIN Logins LO ON
    LO.action = 'logout' AND
    CAST(LO.created AS DATE) = CAST(LI.created AS DATE)
    JOIN dbo.Employees AS E ON E.ID = li.Emp_ID

WHERE
    LI.action = 'login'
GROUP BY
    CAST(LI.created AS DATE), E.fname + ' ' + E.lname, li.Emp_ID

但结果不正确。

  1. 请注意,不同用户的最后两个结果是相同的。例如09:02:28出现两次而不是9:01:58

  2. 此外,对于没有注销emp_id = 3的登录,我遇到了问题。当应用程序意外关闭时会发生这种情况。

  3. 3如何在没有注销的情况下放置00:00:00 4.或者你对这种情况下该怎么做的建议是什么?

    我需要选择一个如下所示的结果集:

    |    created |    login |   logout |    Employee | Emp_ID |
    |------------|----------|----------|-------------|--------|
    | 2016-01-20 | 10:44:42 | 18:30:13 | James Brown |      1 |
    | 2016-01-21 | 08:46:28 | 13:46:46 | James Brown |      1 |
    | 2016-01-22 | 08:49:21 | 22:22:23 | James Brown |      1 |
    | 2016-01-22 | 14:42:06 | 00:00:00 |    Kemi Ojo |      3 |
    | 2016-01-23 | 08:01:47 | 09:01:58 | James Brown |      1 |
    | 2016-01-23 | 08:02:06 | 09:02:28 |    Kemi Ojo |      3 |
    

    SQL fiddle

3 个答案:

答案 0 :(得分:3)

首先,对完美问题的赞誉。表结构,小提琴演示和预期输出有很大帮助。

现在我尝试了这个,它正在努力工作。请重新检查并告诉我。

select t_login.emp_id,t_login.dt_created as created,t_login.login,
case when t_logout.logout is null 
    then cast('00:00:00' as time)
else t_logout.logout end as logout,
e.fname+' '+e.lname Employee
from 
    (select emp_id,CAST(created AS DATE) AS dt_created,
    MIN(CAST(created AS TIME)) as login
    from logins
    where action='login'
    group by emp_id,CAST(created AS DATE)) t_login
left join
    (select emp_id,CAST(created AS DATE) AS dt_created,
    max(CAST(created AS TIME)) as logout
    from logins
    where action='logout'
    group by emp_id,CAST(created AS DATE)) t_logout
        on t_login.emp_id=t_logout.emp_id
        and t_login.dt_created=t_logout.dt_created
inner join 
    employees e
        on e.id=t_login.emp_id

PS:这不会处理特定日期没有登录且仅注销的情况。如果您需要,请使用full outer join并使用与我在case子句中使用的select语句相同的语句。

请参阅此处的小提琴演示

http://sqlfiddle.com/#!3/465f0/34

输出继电器

+---------+-------------+-------------------+-------------------+-------------+
| emp_id  |  created    |      login        |      logout       |  Employee   |
+---------+-------------+-------------------+-------------------+-------------+
|      1  | 2016-01-20  | 10:44:42.0000000  | 18:30:13.0000000  | James Brown |
|      1  | 2016-01-21  | 08:46:28.0000000  | 13:46:46.0000000  | James Brown |
|      1  | 2016-01-22  | 08:49:21.0000000  | 22:22:23.0000000  | James Brown |
|      3  | 2016-01-22  | 14:42:06.0000000  | 00:00:00.0000000  | Kemi Ojo    |
|      1  | 2016-01-23  | 08:01:47.0000000  | 09:01:58.0000000  | James Brown |
|      3  | 2016-01-23  | 08:02:06.0000000  | 09:02:28.0000000  | Kemi Ojo    |
+---------+-------------+-------------------+-------------------+-------------+

答案 1 :(得分:3)

这是我的建议(请注意特定于文化的日期文字。我必须设置一种语言才能获得正确的日期转换):

SET LANGUAGE GERMAN;

DECLARE @logins TABLE(Emp_ID INT,Created DATETIME, Action VARCHAR(100));
INSERT INTO @logins VALUES
 (1,'20/01/2016 10:44:42 AM','login')
,(1,'20/01/2016 4:45:49 PM','logout')
,(1,'20/01/2016 6:30:13 PM','logout')
,(1,'21/01/2016 8:46:28 AM','login')
,(1,'21/01/2016 9:46:42 AM','login')
,(1,'21/01/2016 1:46:46 PM','logout')
,(1,'22/01/2016 8:49:21 AM','login')
,(1,'22/01/2016 1:49:27 PM','logout')
,(1,'22/01/2016 2:29:53 PM','login')
,(1,'22/01/2016 2:30:13 PM','logout')
,(3,'22/01/2016 2:42:06 PM','login')
,(1,'22/01/2016 9:57:22 PM','login')
,(1,'22/01/2016 10:22:23 PM','logout')
,(1,'23/01/2016 8:01:47 AM','login')
,(1,'23/01/2016 9:01:58 AM','logout')
,(3,'23/01/2016 8:02:06 AM','login')
,(3,'23/01/2016 9:02:28 AM','logout');

DECLARE @employees TABLE(ID INT,Fname VARCHAR(100),Lname VARCHAR(100));
INSERT INTO @employees VALUES
 (1,'James','Brown')
,(2,'Mark','Bond')
,(3,'Kemi','Ojo'); 

WITH Logins AS
(
    SELECT
         MIN(PureDate) AS Created
        ,MIN(CAST(l.Created AS TIME)) AS LoginTime
        ,l.Emp_ID
    FROM @logins AS l
    CROSS APPLY(SELECT CAST(l.Created AS DATE) PureDate) AS Created 
    WHERE l.Action ='login'
    GROUP BY l.Emp_ID,PureDate  
)
,Logouts AS
(
    SELECT
         MAX(PureDate) AS Created
        ,MAX(CAST(l.Created AS TIME)) AS LogoutTime
        ,l.Emp_ID
    FROM @logins AS l
    CROSS APPLY(SELECT CAST(l.Created AS DATE) PureDate) AS Created 
    WHERE l.Action ='logout'
    GROUP BY l.Emp_ID,PureDate  
)
SELECT Logins.Created
      ,Logins.LoginTime 
      ,ISNULL(Logouts.LogoutTime,'00:00:00')
      ,e.Lname 
      ,e.ID 
FROM Logins
INNER JOIN @employees AS e ON e.ID = Logins.Emp_ID
LEFT JOIN Logouts ON Logins.Emp_ID = Logouts.Emp_ID 
                 AND Logins.Created = Logouts.Created 
ORDER BY Created,LoginTime 

结果

2016-01-20  10:44:42    18:30:13    Brown   1
2016-01-21  08:46:28    13:46:46    Brown   1
2016-01-22  08:49:21    22:22:23    Brown   1
2016-01-22  14:42:06    00:00:00    Ojo     3
2016-01-23  08:01:47    09:01:58    Brown   1
2016-01-23  08:02:06    09:02:28    Ojo     3

答案 2 :(得分:2)

你错过了LO表和LI表之间的JOIN:

AND LO.Emp_ID = LI.Emp_ID

我认为你可以在不使用自我加入的情况下使用CASE WHEN来做到这一点,我觉得它会更漂亮,但尝试这个补充。

评论编辑: 只需更换:     MAX(CAST(LO.Created AS时间)) ...有:     MAX(CAST(isnull(LO.Created,'00:00:00')AS时间))