SQL确定时间戳是否重叠到多个月并计算每个月的相对时间

时间:2017-11-29 16:08:54

标签: sql sql-server tsql datetime

我对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点开始。

我想确定每个月花在项目上的小时数,这样我就可以按月分组这些时间段。我确定如何实现逻辑。

3 个答案:

答案 0 :(得分:0)

您应该创建一个包含工作日的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

答案 1 :(得分:0)

使用数字表这是相对直接的......

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()到你的心中。

答案 2 :(得分:0)

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

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