【问题标题】:SQL Determining if Time Stamps Overlap Into Multiple Months and Calculating the Relative Time in Each MonthSQL判断时间戳是否重叠为多个月并计算每个月的相对时间
【发布时间】:2018-05-13 10:00:13
【问题描述】:

我对 SQL 很陌生,所以请耐心等待。

我有一个包含各种活动的日期时间戳的数据集。

例如,我有一个包含以下字段的项目列表

start_project [datetime] 
finish_project [datetime] 
verify_project [datetime]

我想确定在项目生命周期的任何时间点,这项工作是否跨越了数月。

例如 start_project 可以在 1 月 1 日上午 9 点开始,finish_project 可以在 2 月 3 日下午 12 点开始,verify_project 可以在 2 月 12 日下午 3 点开始。

我想确定每个月为该项目花费了多少小时,以便我可以按月对这些时间段进行分类。我只是确定如何实现逻辑。

【问题讨论】:

  • 是mysql还是sql server?你能编辑你的标签或更具体的问题吗?
  • @bradbury9 它是 sql 服务器。我已经编辑了我的标签以反映。
  • 我能想到的唯一方法是使用游标并逐个浏览每条记录并确定相关字段是否重叠月份,但我试图尽可能避免使用游标.
  • 您是如何计算小时数的?就像你每天计算 8 小时一样……周末休息,节假日休息?
  • @Zorkolot 一天 24 小时,没有假期或周末。只是每个月的原始总小时数。

标签: sql sql-server tsql datetime


【解决方案1】:

您应该创建一个包含工作日的 dimDate 表。

例如

Date      isBusinessDay
1/1/2017  0
1/2/2017  1
...

然后查询它:

select Month(Date) Month,YEAR(Date) Year, WorkingHours = Count(*)*8 --Assuming 8 bus hours in a day
from DimDate
where Date between StartDate and EndDate
   and isBusinessDay=1
group by Month(Date),YEAR(Date)

要将其添加到您的基本查询中,您需要交叉应用:

select BaseTable.*, a.Month,a.Year, a.WorkingHours
from BaseTable
cross apply(
select Month(Date) Month,YEAR(Date) Year, WorkingHours = Count(*)*8 --Assuming 8 bus hours in a day
    from DimDate
    where Date between BaseTable.StartDate and BaseTable.EndDate
       and isBusinessDay=1
    group by Month(Date),YEAR(Date)) a

【讨论】:

  • 我知道你想要一天 24 小时。只需将 *8 更改为 *24
【解决方案2】:

使用数字表,这相对简单...

https://www.mssqltips.com/sqlservertip/4176/the-sql-server-numbers-table-explained--part-1/

以下查询有效...
- 每个项目开始的月份
- 整个项目跨越多少个月
- 每个跨月返回一行
- 将月份开始日期附加为新字段

SELECT
    yourTable.*,
    CASE
        WHEN months_since_start.id = 0
        THEN yourTable.start_project
        ELSE DATEADD(month, months_since_start.id, first_month.start)
    END
        AS partial_month_start
FROM
    yourTable
CROSS APPLY
(
    VALUES( DATEADD(month, DATEDIFF(month, 0, yourTable.start_project)) )
)
    first_month(start)
INNER JOIN
    dbo.numbers   AS months_since_start
        ON  months_since_start.id >= 0
        AND months_since_start.id <= DATEDIFF(month, yourTable.start_project, yourTable.verify_project)

然后您可以在新行/字段上使用DATEDIFF() 来满足您的需求。

【讨论】:

    【解决方案3】:

    也许我应该使用数字表,但这也有效。

    CREATE TABLE #myTable(ProjName VARCHAR(100), start_project DATETIME, finish_project DATETIME, verify_project DATETIME)
    
    INSERT INTO #myTable SELECT 'proj1', '2017-01-01', '2017-04-15', '2017-04-15'
    INSERT INTO #myTable SELECT 'proj2', '2017-01-02', '2017-01-11', '2017-01-11'
    INSERT INTO #myTable SELECT 'proj3', '2017-06-06', '2017-06-06', '2017-06-06'
    INSERT INTO #myTable SELECT 'proj4', '2017-03-01', '2017-08-15', '2017-08-15'
    
    ;with cte1 AS (
    SELECT CASE 
             WHEN CAST(YEAR(t.start_project) AS VARCHAR) + CAST(MONTH(t.start_project) AS VARCHAR) <> CAST(YEAR(t.finish_project) AS VARCHAR) + CAST(MONTH(t.finish_project) AS VARCHAR)
               THEN (DATEDIFF(DAY, start_project,EOMONTH(start_project, 0)) * 8) + 8
             WHEN start_project = finish_project 
               THEN 8
             ELSE (DATEDIFF(DAY, start_project,finish_project) * 8) + 8
          END AS hours_this_month
          , DATENAME(MONTH,start_project) AS month_name
          , 0 AS month_level, start_project, finish_project, ProjName, verify_project
    FROM #myTable t
    UNION ALL
    SELECT CASE 
             WHEN t.finish_project > EOMONTH(DATEADD(MONTH, t.month_level + 1, t.start_project))
                            AND ( CAST(YEAR(t.start_project) AS VARCHAR) + CAST(MONTH(t.start_project) AS VARCHAR) <> CAST(YEAR(t.finish_project) AS VARCHAR) + CAST(MONTH(t.finish_project) AS VARCHAR))
                THEN
                (DATEDIFF(DAY, CAST(CAST(YEAR(DATEADD(MONTH, t.month_level + 1, t.start_project)) AS VARCHAR) + '-' +
                  CAST(MONTH(DATEADD(MONTH, t.month_level + 1, t.start_project)) AS VARCHAR) + '-01' AS DATETIME)
                         , EOMONTH(DATEADD(MONTH, t.month_level + 1, t.start_project))) * 8) + 8
             ELSE 
                (DATEDIFF(DAY, CAST(CAST(YEAR(DATEADD(MONTH, t.month_level + 1, t.start_project)) AS VARCHAR) + '-' +
                  CAST(MONTH(DATEADD(MONTH, t.month_level + 1, t.start_project)) AS VARCHAR) + '-01' AS DATETIME)
                         , t.finish_project) * 8) + 8
           END
          , DATENAME(MONTH,DATEADD(MONTH, t.month_level + 1, t.start_project))
          , t.month_level + 1
          , t.start_project
          , t.finish_project, t.ProjName, t.verify_project
    FROM cte1 t INNER JOIN 
         #myTable myT ON myT.ProjName = t.ProjName
    WHERE CAST(CAST(YEAR(DATEADD(MONTH, t.month_level + 1, t.start_project)) AS VARCHAR) + '-' +
          CAST(MONTH(DATEADD(MONTH, t.month_level + 1, t.start_project)) AS VARCHAR) + '-01' AS DATETIME)
          <= t.finish_project )  
    SELECT ProjName
         , month_name
         , hours_this_month
         , start_project
         , finish_project
         , verify_project
         , month_level
    FROM cte1
    ORDER BY ProjName, month_level ASC
    

    输出...

    proj1   January  248    2017-01-01 00:00:00.000 2017-04-15 00:00:00.000 2017-04-15 00:00:00.000 0
    proj1   February 224    2017-01-01 00:00:00.000 2017-04-15 00:00:00.000 2017-04-15 00:00:00.000 1
    proj1   March    248    2017-01-01 00:00:00.000 2017-04-15 00:00:00.000 2017-04-15 00:00:00.000 2
    proj1   April    120    2017-01-01 00:00:00.000 2017-04-15 00:00:00.000 2017-04-15 00:00:00.000 3
    proj2   January  80     2017-01-02 00:00:00.000 2017-01-11 00:00:00.000 2017-01-11 00:00:00.000 0
    proj3   June     8      2017-06-06 00:00:00.000 2017-06-06 00:00:00.000 2017-06-06 00:00:00.000 0
    proj4   March    248    2017-03-01 00:00:00.000 2017-08-15 00:00:00.000 2017-08-15 00:00:00.000 0
    proj4   April    240    2017-03-01 00:00:00.000 2017-08-15 00:00:00.000 2017-08-15 00:00:00.000 1
    proj4   May      248    2017-03-01 00:00:00.000 2017-08-15 00:00:00.000 2017-08-15 00:00:00.000 2
    proj4   June     240    2017-03-01 00:00:00.000 2017-08-15 00:00:00.000 2017-08-15 00:00:00.000 3
    proj4   July     248    2017-03-01 00:00:00.000 2017-08-15 00:00:00.000 2017-08-15 00:00:00.000 4
    proj4   August   120    2017-03-01 00:00:00.000 2017-08-15 00:00:00.000 2017-08-15 00:00:00.000 5
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-18
      相关资源
      最近更新 更多