在SQL Server 2017中,根据条件将行转换为列

4

我在SQL Server中有两个表格,我希望将某些行转换为列,按照ID/Name(任何一个都可以)排序,并按Date排序。

[dbo].[USERINFO]

+--------+-------------+---------+
| USERID | BADGENUMBER | NAME    |
+--------+-------------+---------+
| 1      | 1000        | BEN     |
+--------+-------------+---------+
| 2      | 1111        | ANNE    |
+--------+-------------+---------+

[dbo].[CHECKINOUT]:

+--------+-------------------------+
| USERID | CHECKTIME               |
+--------+-------------------------+
| 1      | 2019-02-16 08:01:39.000 |
+--------+-------------------------+
| 1      | 2019-02-16 13:05:21.000 |
+--------+-------------------------+
| 1      | 2019-02-16 14:42:23.000 |
+--------+-------------------------+
| 1      | 2019-02-16 17:07:55.000 |
+--------+-------------------------+
| 1      | 2019-02-18 07:56:23.000 |
+--------+-------------------------+
| 1      | 2019-02-18 19:48:23.000 |
+--------+-------------------------+
| 2      | 2019-02-16 07:43:57.000 |
+--------+-------------------------+
| 2      | 2019-02-16 12:30:04.000 |
+--------+-------------------------+
| 2      | 2019-02-18 06:52:55.000 |
+--------+-------------------------+
| 2      | 2019-02-18 18:01:41.000 |
+--------+-------------------------+
| 2      | 2019-02-19 07:55:17.000 |
+--------+-------------------------+
| 2      | 2019-02-19 12:30:08.000 |
+--------+-------------------------+
| 2      | 2019-02-20 07:52:15.000 |
+--------+-------------------------+
| 2      | 2019-02-20 17:51:49.000 |
+--------+-------------------------+

我希望能得到这样的结果。

+------+------+------------+----------+----------+----------+----------+--------+
| ID   | Name | Date       | Time1    | Time2    | Time3    | Time4    | Time5  |
+------+------+------------+----------+----------+----------+----------+--------+
| 1111 | ANNE | 16/02/2019 | 07:43:57 | 12:30:04 | NULL     | NULL     | NULL   |
+------+------+------------+----------+----------+----------+----------+--------+
| 1111 | ANNE | 18/02/2019 | 06:52:55 | 18:01:41 | NULL     | NULL     | NULL   |
+------+------+------------+----------+----------+----------+----------+--------+
| 1111 | ANNE | 19/02/2019 | 07:55:17 | 12:30:08 | NULL     | NULL     | NULL   |
+------+------+------------+----------+----------+----------+----------+--------+
| 1111 | ANNE | 20/02/2019 | 07:52:15 | 17:51:49 | NULL     | NULL     | NULL   |
+------+------+------------+----------+----------+----------+----------+--------+
| 1000 | BEN  | 16/02/2019 | 08:01:39 | 13:05:21 | 14:42:23 | 17:07:55 | NULL   |
+------+------+------------+----------+----------+----------+----------+--------+
| 1000 | BEN  | 18/02/2019 | 07:56:23 | 19:48:23 | NULL     | NULL     | NULL   |
+------+------+------------+----------+----------+----------+----------+--------+

无论是按 ID 还是按名称排序都可以。

到目前为止,我已经尝试了以下代码:

SELECT *
INTO #Temp
FROM (
        SELECT U.BADGENUMBER as ID, U.[NAME] as Name, 
            CONVERT(VARCHAR(10),C.CHECKTIME, 103) [Date], 
            CONVERT(VARCHAR(8), C.CHECKTIME, 108) [Time]     
            FROM [CHECKINOUT] as C JOIN [USERINFO] as U
            ON C.USERID = U.USERID
) AS x
SELECT ID, Name, Date, [1] as Time1, [2] as Time2, [3] as Time3,
    [4] as Time4, [5] as Time5, [6] as Time6, [7] as Time7, [8] as Time8, [9] as Time9
FROM ( SELECT 
                ID, Name, Date, Time,
                row_number() over (partition by Name order by Date) as rn
                from #Temp
                ) s

PIVOT (
    MAX([Time]) for rn in ([1], [2], [3], [4], [5], [6], [7], [8], [9])
    ) as pvt
ORDER BY ID

DROP TABLE #Temp

基于此链接进行翻译:link
但我得到的结果是这样的:
+------+------+------------+----------+----------+----------+----------+----------+
| ID   | Name | Date       | Time1    | Time2    | Time3    | Time4    | Time5    |
+------+------+------------+----------+----------+----------+----------+----------+
| 1111 | ANNE | 16/02/2019 | 07:43:57 | 12:30:04 | NULL     | NULL     | NULL     |
+------+------+------------+----------+----------+----------+----------+----------+
| 1111 | ANNE | 18/02/2019 | NULL     | NULL     | 06:52:55 | 18:01:41 | NULL     |
+------+------+------------+----------+----------+----------+----------+----------+
| 1111 | ANNE | 19/02/2019 | NULL     | NULL     | NULL     | NULL     | 07:55:17 |
+------+------+------------+----------+----------+----------+----------+----------+
| 1111 | ANNE | 20/02/2019 | NULL     | NULL     | NULL     | NULL     | NULL     |
+------+------+------------+----------+----------+----------+----------+----------+
| 1000 | BEN  | 16/02/2019 | 08:01:39 | 13:05:21 | 14:42:23 | 17:07:55 | NULL     |
+------+------+------------+----------+----------+----------+----------+----------+
| 1000 | BEN  | 18/02/2019 | NULL     | NULL     | NULL     | NULL     | 07:56:23 |
+------+------+------------+----------+----------+----------+----------+----------+


我哪里做错了?请指出来,谢谢。敬礼。

这就是我所说的一个很好的问题! - jarlh
2个回答

1
这个问题来自于ROW_NUMBER()函数的OVER()子句。你需要按照日期分区,而不仅仅是按用户分区。此外,你需要按照[Time]排序。
你需要更改以下内容:
row_number() over (partition by Name order by Date) as rn

至:

row_number() over (partition by  [Date], Name order by [Time]) as rn as rn

DB Fiddle演示:

  ID | 名称 | 日期       | 时间1    | 时间2    | 时间3    | 时间4    | 时间5 | 时间6 | 时间7 | 时间8 | 时间9
---: | :--- | :--------- | :------- | :------- | :------- | :------- | :---- | :---- | :---- | :---- | :----
1000 | BEN  | 2019年2月16日 | 08:01:39 | 13:05:21 | 14:42:23 | 17:07:55 | null  | null  | null  | null  | null 
1000 | BEN  | 2019年2月18日 | 07:56:23 | 19:48:23 | null     | null     | null  | null  | null  | null  | null 
1111 | ANNE | 2019年2月16日 | 07:43:57 | 12:30:04 | null     | null     | null  | null  | null  | null  | null 
1111 | ANNE | 2019年2月18日 | 06:52:55 | 18:01:41 | null     | null     | null  | null  | null  | null  | null 
1111 | ANNE | 2019年2月19日 | 07:55:17 | 12:30:08 | null     | null     | null  | null  | null  | null  | null 
1111 | ANNE | 2019年2月20日 | 07:52:15 | 17:51:49 | null     | null     | null  | null  | null  | null  | null 

此外,我建议使用条件聚合而不是PIVOT来解决这个问题。后者是特定于供应商的,而前者受到大多数关系型数据库管理系统的支持。我还发现这种语法更易于阅读:
SELECT
    badgenumber, 
    name,
    [Date],
    MAX(CASE WHEN rn = 1 THEN [Time] END) AS Time1,
    MAX(CASE WHEN rn = 2 THEN [Time] END) AS Time2,
    MAX(CASE WHEN rn = 3 THEN [Time] END) AS Time3,
    MAX(CASE WHEN rn = 4 THEN [Time] END) AS Time4,
    MAX(CASE WHEN rn = 5 THEN [Time] END) AS Time5
FROM (
    SELECT 
        u.badgenumber, 
        u.name, 
        CAST(checktime AS DATE) as [Date],
        CAST(checktime AS TIME) as [Time],
        ROW_NUMBER() OVER(PARTITION BY u.badgenumber, CAST(checktime AS DATE) ORDER BY c.checktime) rn
    FROM userinfo u
    INNER JOIN checkinout c ON c.userid = u.userid
) x
GROUP BY badgenumber, name, [Date]

DB Fiddle上的演示


0

这应该可以工作

DECLARE @USERINFO TABLE (USERID INT,BADGENUMBER INT, [NAME] VARCHAR(50))
DECLARE @CHECKINOUT TABLE (USERID INT,CHECKTIME DATETIME)
INSERT INTO @USERINFO VALUES
(1,1000, 'BEN '),
(2,1111, 'ANNE')
INSERT INTO @CHECKINOUT VALUES
(1,'2019-02-16 08:01:39.000'),
(1,'2019-02-16 13:05:21.000'),
(1,'2019-02-16 14:42:23.000'),
(1,'2019-02-16 17:07:55.000'),
(1,'2019-02-18 07:56:23.000'),
(1,'2019-02-18 19:48:23.000'),
(2,'2019-02-16 07:43:57.000'),
(2,'2019-02-16 12:30:04.000'),
(2,'2019-02-18 06:52:55.000'),
(2,'2019-02-18 18:01:41.000'),
(2,'2019-02-19 07:55:17.000'),
(2,'2019-02-19 12:30:08.000'),
(2,'2019-02-20 07:52:15.000'),
(2,'2019-02-20 17:51:49.000')

SELECT *
INTO #Temp
FROM (
        SELECT U.BADGENUMBER as ID, U.[NAME] as Name, 
            CONVERT(VARCHAR(10),C.CHECKTIME, 103) [Date], 
            CONVERT(VARCHAR(8), C.CHECKTIME, 108) [Time]     
            FROM @CHECKINOUT as C JOIN @USERINFO as U
            ON C.USERID = U.USERID
) AS x

SELECT ID, [Name], Date, [1] as Time1, [2] as Time2, [3] as Time3,
    [4] as Time4, [5] as Time5, [6] as Time6, [7] as Time7, [8] as Time8, [9] as Time9
FROM ( SELECT 
                ID, Name, Date, Time,
                row_number() over (partition by  [Date], Name order by [Time]) as rn 
                from #Temp
                ) s
PIVOT (
    MAX([Time]) for rn in ([1], [2], [3], [4], [5], [6], [7], [8], [9])
    ) as pvt
ORDER BY ID DESC;
DROP TABLE #Temp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接