【问题标题】:sql timesheet count by day for the weeksql timesheet 按天计算一周
【发布时间】:2011-08-16 16:17:49
【问题描述】:

我的桌子是这样的:

select clocktime, for_UID, in1_out0 from timeclockentries

clocktime                   for_UID    in1_out0
2011-08-07 15:13:58.390 user193    1
2011-08-07 21:09:45.093 user193    0
2011-08-09 14:10:00.000 user193    1
2011-08-09 20:10:00.000 user193    0

我希望结果看起来像(假设一周的开始是星期六),由名为“day1”、“day2”等的列分隔......(但为了便于阅读,我用换行符输入了它们) :

day1                       day2                       day3
1900-01-01 00:00:00.000    1900-01-01 05:55:46.700    1900-01-01 00:00:00.000   

day4                       day5                       day6
1900-01-01 06:00:00.000    1900-01-01 00:00:00.000    1900-01-01 00:00:00.000

day7
1900-01-01 00:00:00.000

(我用的是sql2005)

以下是我一天使用的:

CREATE PROCEDURE [dbo].[sp_gethoursbyday]
  @whichforUID varchar(20),
  @whichdate datetime
AS
BEGIN

;WITH CTE as(
SELECT 
    DENSE_RANK() over (Partition by for_UID , in1_out0  Order by clocktime) id,
    clocktime,
    for_UID,
    in1_out0

FROM 
    kdhcastle.dbo.timeclockentries tc
WHERE 
         tc.for_UID = @whichforUID 
and month(tc.[clocktime]) = month(@whichdate)
and day(tc.[clocktime]) = day(@whichdate)
and year(tc.[clocktime]) = year(@whichdate)

    )
SELECT
     Cast(cast(sum(
        cast(outTime.clocktime as float) - cast(inTime.clocktime as float)
        )as datetime) as datetime) as 'hoursbydy'
FROM 
     CTE inTime
     INNER JOIN CTE outTime
     ON inTime.for_UID = outTime.for_UID
         AND inTime.id = outTime.id
        AND inTime.in1_out0 = 1
        and outTime.in1_out0 = 0

    END

【问题讨论】:

  • 是否可以保证每个用户每天总是恰好有 0 或 2 条记录,并且数据只会涵盖 7 天的时间段?您想将 UID 作为列添加到您的新输出中吗?
  • 输出不需要 UID。每个用户每天可能有无限数量的记录。总是 7 天。
  • 还有其他(更重要的)问题吗?

标签: sql sql-server sql-server-2005


【解决方案1】:
SELECT
  SUM(CASE WHEN DayOfWeek = 1 THEN Duration ELSE 0 END)    AS Day1,
  SUM(CASE WHEN DayOfWeek = 2 THEN Duration ELSE 0 END)    AS Day2,
  SUM(CASE WHEN DayOfWeek = 3 THEN Duration ELSE 0 END)    AS Day3,
  SUM(CASE WHEN DayOfWeek = 4 THEN Duration ELSE 0 END)    AS Day4,
  SUM(CASE WHEN DayOfWeek = 5 THEN Duration ELSE 0 END)    AS Day5,
  SUM(CASE WHEN DayOfWeek = 6 THEN Duration ELSE 0 END)    AS Day6,
  SUM(CASE WHEN DayOfWeek = 7 THEN Duration ELSE 0 END)    AS Day7
FROM
(
  SELECT
    DATEDIFF(DAY, '2011 Jan 01', clocktime) % 7 + 1  AS DayOfWeek,
    CAST(MAX(clocktime) - MIN(clocktime) AS FLOAT)   AS Duration
  FROM
    yourTable
  GROUP BY
    for_UID,
    DATEDIFF(DAY, '2011 Jan 01', clocktime)
)
  AS [data]

【讨论】:

  • 已更改为将DATEDIFF 设置为已知的星期六。 % 7 然后在周六到周五产生 0-6,所以 +1 给出 1-7。不同周的值汇总到一条记录中。
  • 我建议使用安全的日期格式。 “2011 Jan 01”在美国/英国英语以外的许多文化中都会失败,而“20110101”永远不会失败。
  • 我错过了问题的 cmets,对不起。 +1 解决方案。
【解决方案2】:

这更冗长,但我的重点是 (a) 避免重复表达式和 (b) 模拟旨在提供给存储过程的所有输入参数,以便根据所需的用户/日期过滤结果。请注意,@whichdate 参数将返回到前一个星期六的午夜,无论它是一周中的哪一天或与之相关联的时间。

输入参数:

DECLARE @whichdate DATETIME;
SET @whichdate = '2011-08-08T12:34:00';

DECLARE @whichforUID VARCHAR(32);
SET @whichforUID = 'user193';

正文(只需注释掉 DECLARE @t / INSERT @t 行,并将第一个 CTE 中的 @t 更改为真实表名:

SET @whichdate = DATEADD(DAY, -DATEPART(WEEKDAY, @whichdate), @whichdate);
SET @whichdate = DATEADD(DAY, 0, DATEDIFF(DAY, 0, @whichdate));

DECLARE @t TABLE(clocktime DATETIME, for_UID VARCHAR(32), in1_out0 BIT);

INSERT @t SELECT '2011-08-07 15:13:58.390','user193',1
UNION ALL SELECT '2011-08-07 21:09:45.093','user193',0
UNION ALL SELECT '2011-08-09 14:10:00.000','user193',1
UNION ALL SELECT '2011-08-09 20:10:00.000','user193',0;

WITH s(dw, ct, in1_out0) AS
(
    SELECT 1 + (DATEDIFF(DAY, '2011-01-01', clocktime) % 7), 
        clocktime, in1_out0 FROM @t
        where for_UID = @whichforUID
        AND clocktime >= @whichdate
        AND clocktime < DATEADD(DAY, 7, @whichdate)
),
d(dw, min_ct, max_ct) AS 
(
    SELECT dw, 
        MIN(CASE WHEN in1_out0 = 1 THEN ct ELSE NULL END),
        MAX(CASE WHEN in1_out0 = 0 THEN ct ELSE NULL END)
    FROM s GROUP BY dw
),
x AS 
(
    SELECT d = DATEADD(MILLISECOND, DATEDIFF(MILLISECOND, min_ct, max_ct), 0), 
        dw FROM d
),
pvt AS (
    SELECT * FROM x PIVOT
    (MAX(d) FOR dw IN ([1],[2],[3],[4],[5],[6],[7])) AS p
)
SELECT
    day1 = COALESCE([1], '19000101'),
    day2 = COALESCE([2], '19000101'),
    day3 = COALESCE([3], '19000101'),
    day4 = COALESCE([4], '19000101'),
    day5 = COALESCE([5], '19000101'),
    day6 = COALESCE([6], '19000101'),
    day7 = COALESCE([7], '19000101')
FROM pvt;

【讨论】:

  • 这条线是干什么用的:>>SELECT DATEDIFF(DAY, '2010-12-31', clocktime) % 7,为什么有那个日期?
  • 这只是一个任意的,已知的星期日。
  • 是的,抱歉,我的意思是星期五(加一个而不是减一个)。无论如何,这样做的原因只是为了避免将 1 月 1 日作为基准日期所需的 +1(因为 1 月 1 日是星期六)。我只匹配操作要求的内容 - 似乎他希望周日的数据 (2011-08-07) 属于“day2”,因为他的一周开始是周六。
  • 问题在于 % 7 产生 0-6,而不是 1-7。所以,星期六确实是 1,但星期五是 0 而不是 7。
  • 明白了,已更正。我确信样本数据未涵盖其他边缘情况(例如,没有时钟输出的时钟输入,反之亦然)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-10
  • 1970-01-01
  • 1970-01-01
  • 2011-01-06
  • 1970-01-01
  • 2010-09-22
相关资源
最近更新 更多