创建包括零小时周在内的用户时间报告

时间:2019-05-17 23:35:07

标签: sql sql-server

我花了很长时间来整理我认为很简单的查询。我有一个表,该表记录了在某项任务上花费的总时间以及报告这些时间的用户。我需要汇总一个查询,该查询返回给定用户向一年中的每个星期收费的小时数(包括不收费的几周)。

预期输出:

    |USER_ID | START_DATE | END_DATE  | HOURS |
    -------------------------------------------
    |'JIM'   | 4/28/2019  | 5/4/2019  |   6   |    
    |'JIM'   | 5/5/2019   | 5/11/2019 |   0   |
    |'JIM'   | 5/12/2019  | 5/18/2019 |   16  |

我有一个返回每天的开始和结束日期的函数,因此我使用了该函数,并按日期将其加入任务表并汇总了小时数。这让我非常接近,但是由于我要参加约会,所以显然所有零小时行的USER_ID最终都为NULL。

当前输出:

|USER_ID | START_DATE | END_DATE  | HOURS |
-------------------------------------------
|'JIM'   | 4/28/2019  | 5/4/2019  |   6   |    
| NULL   | 5/5/2019   | 5/11/2019 |   0   |
|'JIM'   | 5/12/2019  | 5/18/2019 |   16  |

我尝试了其他几种方法,但是每次遇到相同的问题时,我都会尝试。有什么想法吗?

模式:

---------------------------------
|          TASK_LOG             |
---------------------------------    
|USER_ID | DATE_ENTERED | HOURS |
-------------------------------
|'JIM'   | 4/28/2019    |   6   |    
|'JIM'   | 5/12/2019    |   6   |
|'JIM'   | 5/13/2019    |   10  |

------------------------------------
|       DATE_HELPER_TABLE           |
|(This is actually a function, but I| 
| put it in a table to simplify)    | 
-------------------------------------    
|DATE | START_OF_WEEK | END_OF_WEEK |
-------------------------------------
|5/3/2019 | 4/28/2019  |  5/4/2019  |    
|5/4/2019 | 4/28/2019  |  5/4/2019  |
|5/5/2019 | 5/5/2019   |  5/11/2019 |
| ETC ...                           |

查询:

 SELECT HRS.USER_ID
        ,DHT.START_OF_WEEK
        ,DHT.END_OF_WEEK
        ,SUM(HOURS)
    FROM DATE_HELPER_TABLE DHT
    LEFT JOIN (
        SELECT TL.USER_ID
            ,TL.HOURS
            ,DHT2.START_OF_WEEK
            ,DHT2.END_OF_WEEK
        FROM TASK_LOG TL
        JOIN DATE_HELPER_TABLE DHT2 ON DHT2.DATE_VALUE = TL.DATE_ENTERED
        WHERE TL.USER_ID = 'JIM1'
        ) HRS ON HRS.START_OF_WEEK = DHT.START_OF_WEEK
    GROUP BY USER_ID
        ,DHT.START_OF_WEEK
        ,DHT.END_OF_WEEK
    ORDER BY DHT.START_OF_WEEK

http://sqlfiddle.com/#!18/02d43/3(注意:对于此sql小提琴,我将日期帮助程序函数转换为表格以简化操作)

3 个答案:

答案 0 :(得分:3)

Cross join the users (in question) and include them in the join condition. Use coalesce() to get 0 instead of NULL for the hours of weeks where no work was done.

SELECT u.user_id,
       dht.start_of_week,
       dht.end_of_week,
       coalesce(sum(hrs.hours), 0)
       FROM date_helper_table dht
            CROSS JOIN (VALUES ('JIM1')) u (user_id)
            LEFT JOIN (SELECT tl.user_id,
                              dht2.start_of_week,
                              tl.hours
                              FROM task_log tl
                                   INNER JOIN date_helper_table dht2
                                              ON dht2.date_value = tl.date_entered) hrs
                      ON hrs.user_id = u.user_id
                         AND hrs.start_of_week = dht.start_of_week
       GROUP BY u.user_id,
                dht.start_of_week,
                dht.end_of_week
       ORDER BY dht.start_of_week;

I used a VALUES clause here to list the users. If you only want to get the times for particular users you can do so too (or use any other subquery, or ...). Otherwise you can use your user table (which you didn't post, so I had to use that substitute).

However the figures that are produced by this (and your original query) look strange to me. In the fiddle your user has worked for a total of 23 hours in the task_log table. Yet your sums in the result are 24 and 80, that is way to much on its own and even worse taking into account, that 1 hour in task_log isn't even on a date listed in date_helper_table.

I suspect you get more accurate figures if you just join task_log, not that weird derived table.

SELECT u.user_id,
       dht.start_of_week,
       dht.end_of_week,
       coalesce(sum(tl.hours), 0)
       FROM date_helper_table dht
            CROSS JOIN (VALUES ('JIM1')) u (user_id)
            LEFT JOIN task_log tl
                      ON tl.user_id = u.user_id
                         AND tl.date_entered = dht.date_value
       GROUP BY u.user_id,
                dht.start_of_week,
                dht.end_of_week
       ORDER BY dht.start_of_week;

But maybe that's just me.

SQL Fiddle

一起使用

答案 1 :(得分:1)

http://sqlfiddle.com/#!18/02d43/65

使用您的SQL提琴,我仅更新了select语句以说明并转换空值。据我所知,您的帖子中没有任何内容使此选项不可行。如果不是这种情况,请告诉我,我将进行更新。 (这并不是要降低粘性位的答案,而是提供一种替代方法)

SELECT ISNULL(HRS.USER_ID, '') as [USER_ID]
    ,DHT.START_OF_WEEK
    ,DHT.END_OF_WEEK
    ,SUM(ISNULL(HOURS,0)) as [SUM]
FROM DATE_HELPER_TABLE DHT
LEFT JOIN (
    SELECT TL.USER_ID
        ,TL.HOURS
        ,DHT2.START_OF_WEEK
        ,DHT2.END_OF_WEEK
    FROM TASK_LOG TL
    JOIN DATE_HELPER_TABLE DHT2 ON DHT2.DATE_VALUE = TL.DATE_ENTERED
    WHERE TL.USER_ID = 'JIM1'
    ) HRS ON HRS.START_OF_WEEK = DHT.START_OF_WEEK
GROUP BY USER_ID
    ,DHT.START_OF_WEEK
    ,DHT.END_OF_WEEK
ORDER BY DHT.START_OF_WEEK

答案 2 :(得分:0)

在第一列中创建一个日期表,其中包括接下来100年的所有日期,在下一列中包括一年中的星期几,每月的某天等。

然后从该日期表中进行选择,然后将其他所有内容连接起来。使用notull函数将零替换为零。