如果SQL Server中的两列之间存在差异,则生成更多行

时间:2014-02-03 20:51:24

标签: sql sql-server

我目前正在研究MS Project Server的一些报告,发现这个奇怪之处:<​​/ p>

由于某些不明原因,每当您在连续几天内指定相同的任务时间时,应用程序会更新开始日期和结束时,而不是为每个约会创建条目数据库上的日期字段,只留下该任务的一个条目,但日期之间的范围。

如果连续几天指定给任务的时间不同,则每次约会将创建一个条目。

(是的,我知道,这有点令人困惑。我甚至不知道如何更好地解释这一点。)

我想知道在开始和结束日期之间存在差异时,是否有可能在SQL语句中生成更多行,范围内每天一个

这是我现在的查询,我已经知道哪些行有这个日期差异,但我不知道接下来我能做什么。

select 
        r.WRES_ID, r.RES_NAME, PROJ_NAME, p.WPROJ_ID, TASK_NAME, WWORK_VALUE, WWORK_START, WWORK_FINISH,
        datediff(d, WWORK_START, WWORK_FINISH) + 1 AS work_days
    from MSP_WEB_RESOURCES r
    join
    MSP_WEB_ASSIGNMENTS a on a.WRES_ID = r.WRES_ID
    join
    MSP_WEB_PROJECTS p on p.WPROJ_ID = a.WPROJ_ID
    join
    MSP_WEB_WORK w on w.WASSN_ID = a.WASSN_ID
    where RES_NAME = 'HenriqueBarcelos'
    and WWORK_TYPE = 1
    and WWORK_VALUE > 0
    and WWORK_FINISH between '2014-01-27' and '2014-01-31'
    order by WWORK_FINISH DESC

我知道我可以在应用程序级别执行此操作,但我想知道是否可以在数据库本身内执行此操作。

提前感谢。

编辑:

这些是我目前的结果:

WRES_ID | RES_NAME         | TASK_NAME               | WWORK_VALUE   | WWORK_START         | WWORK_FINISH        | work_days
--------+------------------+-------------------------+---------------+---------------------+---------------------+----------
382     | HenriqueBarcelos | Outsourcing Initiatives | 60000.000000  | 2014-01-30 00:00:00 | 2014-01-30 00:00:00 | 1
382     | HenriqueBarcelos | Internal Training       | 289800.000000 | 2014-01-29 00:00:00 | 2014-01-29 00:00:00 | 1
382     | HenriqueBarcelos | Outsourcing Initiatives | 120000.000000 | 2014-01-29 00:00:00 | 2014-01-29 00:00:00 | 1
382     | HenriqueBarcelos | Outsourcing Initiatives | 60000.000000  | 2014-01-27 00:00:00 | 2014-01-28 00:00:00 | 2
382     | HenriqueBarcelos | Infrastructure (TI)     | 120000.000000 | 2014-01-27 00:00:00 | 2014-01-27 00:00:00 | 1

请注意,倒数第二个寄存器的范围为2天。根据契约,有2个任命,一个在1月27日,另一个在28日。

我想要做的是扩展这个并在这种情况下每天返回一个条目。

3 个答案:

答案 0 :(得分:0)

可以做到,但不是很优雅。首先,您需要一个将日期范围扩展为日期序列的函数:

CREATE FUNCTION ufn_Expand(@start DATE, @end DATE)
RETURNS TABLE   
AS
RETURN
WITH cte AS
(
    SELECT @start AS dt

    UNION ALL

    SELECT DATEADD(dd, 1, dt) FROM cte WHERE dt < @end
)
SELECT dt FROM cte

然后在CROSS APPLY的查询中使用它:

SELECT /* your columns */, x.dt
FROM /* your joins */
CROSS APPLY ufn_Expand(WWORK_START, WWORK_FINISH) x

答案 1 :(得分:0)

我会使用数字表(很好,基于集合,yum!)

SELECT start_date
     , end_date
     , DateDiff(dd, start_date, end_date) + 1 As number_of_days --rows to display
FROM   your_table
 INNER
  JOIN dbo.numbers
    ON numbers.number BETWEEN 1 AND DateDiff(dd, start_date, end_date) + 1

使用您最喜欢的搜索引擎查找数字表脚本。 Here's one I made earlier

顺便说一句:如果删除+1 s,你只需将连接修改为零和DateDiff()之间 - 我添加了+ 1s,因为我认为它可能更清晰!

答案 2 :(得分:0)

您可以从另一个角度看到这一点。你不是每个工作日都想要一排。您真正需要的是工作天数乘以报告的工作时间。像这样:

(dbo.MSP_WEB_WORK.WWORK_VALUE / 60000) * (DATEDIFF(day, dbo.MSP_WEB_WORK.WWORK_START, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1)
然而,这会产生一个问题。让我们说你想要一个特定的时期。如果您使用WWORK_START和WWORK_FINISH日期作为报告,则需要小心将所有工作包含在该期间内的某些日期。这样的事情会做到:

DECLARE @InitDate DATETIME;
DECLARE @EndDate DATETIME;

SET @InitDate = '2016/06/01';
SET @EndDate = '2016/07/01';

--Full list of tasks
SELECT  dbo.MSP_WEB_RESOURCES.RES_NAME AS Name, dbo.MSP_WEB_PROJECTS.PROJ_NAME AS Project, 
        dbo.MSP_WEB_WORK.WWORK_VALUE / 60000 AS ReportedWork,
        CASE
            WHEN WWORK_START < @InitDate THEN DATEDIFF(day, @InitDate, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1 --If the task started before the start of the period
            WHEN WWORK_FINISH > DATEDIFF(day,-1,@EndDate) THEN DATEDIFF(day, WWORK_START, DATEDIFF(day,-1,@EndDate)) + 1 --if the task ended after the end of the period
            ELSE DATEDIFF(day, dbo.MSP_WEB_WORK.WWORK_START, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1 --All tasks with start and end date inside the period
        END AS RepeatedDays,        
        CASE
            WHEN WWORK_START < @InitDate THEN (dbo.MSP_WEB_WORK.WWORK_VALUE / 60000) * (DATEDIFF(day, @InitDate, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1)
            WHEN WWORK_FINISH > DATEDIFF(day,-1,@EndDate) THEN (dbo.MSP_WEB_WORK.WWORK_VALUE / 60000) * (DATEDIFF(day, WWORK_START, DATEDIFF(day,-1,@EndDate)) + 1)
            ELSE (dbo.MSP_WEB_WORK.WWORK_VALUE / 60000) * (DATEDIFF(day, dbo.MSP_WEB_WORK.WWORK_START, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1)
        END AS ActualWork,
        dbo.MSP_WEB_WORK.WWORK_START, 
        dbo.MSP_WEB_WORK.WWORK_FINISH
FROM    dbo.MSP_WEB_RESOURCES INNER JOIN
        dbo.MSP_WEB_ASSIGNMENTS INNER JOIN
        dbo.MSP_WEB_PROJECTS ON dbo.MSP_WEB_ASSIGNMENTS.WPROJ_ID = dbo.MSP_WEB_PROJECTS.WPROJ_ID INNER JOIN
        dbo.MSP_WEB_WORK ON dbo.MSP_WEB_ASSIGNMENTS.WASSN_ID = dbo.MSP_WEB_WORK.WASSN_ID ON 
        dbo.MSP_WEB_RESOURCES.WRES_ID = dbo.MSP_WEB_ASSIGNMENTS.WRES_ID
WHERE   (dbo.MSP_WEB_WORK.WWORK_TYPE = 1) AND 
        (
            @InitDate BETWEEN dbo.MSP_WEB_WORK.WWORK_START and dbo.MSP_WEB_WORK.WWORK_FINISH OR
            DATEADD(day,-1,@EndDate) BETWEEN dbo.MSP_WEB_WORK.WWORK_START and dbo.MSP_WEB_WORK.WWORK_FINISH OR
            (dbo.MSP_WEB_WORK.WWORK_START >= @InitDate) AND
            (dbo.MSP_WEB_WORK.WWORK_FINISH < @EndDate)
        )
ORDER BY dbo.MSP_WEB_WORK.WWORK_START;