2015-06-19 69 views
1

我使用SQL Server和我有如下表:如果日期按顺序,如何基于日期列合并SQL中的行?

ID  Res_ID Start  End 
--------------------------------------- 
5917 9742 2015-08-11 2015-08-11 
5918 9742 2015-08-12 2015-08-12 
5919 9837 2015-09-07 2015-09-07 
5920 9788 2015-09-09 2015-09-09 
5921 9788 2015-09-10 2015-09-10 
5922 9788 2015-09-11 2015-09-11 
5923 9788 2015-09-11 2015-09-11 
5924 9788 2015-10-01 2015-10-01 
5925 9788 2015-10-02 2015-10-02 

我尝试在日期顺序合并行。在我的例子中,查询后我应该得到下面的表格:

ID  Res_ID Start  End 
---------------------------------------- 
XXXX 9742 2015-08-11 2015-08-12 
XXXX 9837 2015-09-07 2015-09-07 
XXXX 9788 2015-09-09 2015-09-11 
XXXX 9788 2015-10-01 2015-10-02 

我真的不是最后的[ID]。

我不知道该怎么做... 你能帮我做这个手术吗?

编辑:天必须是连续的,并在源表中开始=结束。我更新了我的例子。

+1

结束日期应该是09-10和09-11而不是10-09和11-09? –

+0

@JamesZ确实,我的不好。我更新了它,谢谢! – Alex

回答

2

这听起来像一个缺口和岛屿问题。假设你的范围总是只有1天,你可以用行号来计算从组的第一天到这一行的距离。所有那些具有相同的距离(如扣除行号之日起结束到当天)属于同一个岛上,否则这是一个缺口:

select 
    min(id) as id, 
    res_id, 
    min([start]) as [start], 
    max([end]) as [end] 
from (
    select 
     id, 
     res_id, 
     [start], 
     [end], 
     dateadd(day, -RN, [end]) as groupdate 
    from 
    (
     select 
     id, 
     res_id, 
     [start], 
     [end], 
     row_number() over (partition by res_id order by [start] asc) as RN 
     from 
     table1 
    ) X 
) Y 
group by 
    res_id, 
    groupdate 

您可以在SQL Fiddle检查。我在日期之间添加了一行额外的行,以确保日期与其他日期没有分组。

+0

非常令人印象深刻,非常感谢! – Alex

0

你可以使用这样的事情:

SELECT DISTINCT 
Min([Start]) OVER(Partition By Res_ID Order By [Start] Asc) as 'Start', 
Max([End]) OVER(Partition By Res_ID Order By [End] Desc) as 'End' 
FROM sometable 

这完美的作品。但是为了使它起作用,你不能选择第一个ID,因为它在你的例子中被压制了,所以它无用。

SQL FIDDLE

+1

这会让你走上正确的道路。您的实际查询可能涉及从选择中选择以获得您想要的内容。 – TheMadDBA

+0

@TheMadDBA我已经调整并测试了它,现在它工作正常。 –

+0

我认为他们也需要所有其他值......并且可能只有当日期是连续的(相隔1天)时才有效?但是至少你应该让它们在正确的路径上加上窗口函数。 – TheMadDBA

0

对于您的示例提供的精确数据集,你可以,如果你RES_ID的值的变化到另一个RES_ID后出现使用MIN和MAX ...

SELECT 
    DerivedFirstEvents.[ID], 
    DerivedFirstEvents.Res_ID, 
    DerivedTotalDurations.[Start], 
    DerivedTotalDurations.[End] 
FROM 
    (
    SELECT 
     MIN([ID]) AS [ID], 
     Res_ID 
    FROM 
     SampleTable 
    GROUP BY 
     Res_ID 
    ) DerivedFirstEvents 
    LEFT OUTER JOIN 
    (
    SELECT 
     Res_ID, 
     MIN(Start) AS [Start], 
     MAX([End]) AS [End] 
    FROM 
     SampleTable 
    GROUP BY 
     Res_ID 
    ) DerivedTotalDurations ON DerivedFirstEvents.Res_ID = DerivedTotalDurations.Res_ID 

然而, ,像这样...

ID  Res_ID Start  End 
--------------------------------------- 
5917 9742 2015-08-11 2015-08-11 
5918 9742 2015-08-12 2015-08-12 
5919 9837 2015-09-07 2015-09-07 
5920 9788 2015-09-09 2015-09-09 
5921 9788 2015-09-10 2015-10-09 
5922 9788 2015-09-11 2015-11-09 
5923 9742 2015-10-11 2015-10-11 --new line 
5924 9742 2015-10-12 2015-10-12 --new line 

...你的结果为9742 RES_ID设置将出现开始就2015年8月11日,结束于2015年10月12日,这可能不是所期望的结果。如果是这种情况,我认为你会有一个更复杂的解决方案,包括在记录集中运行游标并检查每行以设置一些变量,然后有条件地将它们插入临时表中,然后从该临时表中选择表完成后。

0

试试这个!

IF OBJECT_ID(N'tempdb..#t') IS NOT NULL DROP TABLE #t 

;WITH u ([id],[Res_ID],[Start],[End]) AS (SELECT N'5917' AS [id], N'9742' AS [Res_ID], N'2015-08-11' AS [Start], N'2015-08-11' AS [End] 
UNION SELECT N'5918' AS [id], N'9742' AS [Res_ID], N'2015-08-12' AS [Start], N'2015-08-12' AS [End] 
UNION SELECT N'5919' AS [id], N'9837' AS [Res_ID], N'2015-09-07' AS [Start], N'2015-09-07' AS [End] 
UNION SELECT N'5920' AS [id], N'9788' AS [Res_ID], N'2015-09-09' AS [Start], N'2015-09-09' AS [End] 
UNION SELECT N'5921' AS [id], N'9788' AS [Res_ID], N'2015-09-10' AS [Start], N'2015-10-09' AS [End] 
UNION SELECT N'5922' AS [id], N'9788' AS [Res_ID], N'2015-09-11' AS [Start], N'2015-11-09' AS [End]) 
SELECT 
    u.[id], 
    u.[Res_ID], 
    u.[Start], 
    u.[End] 
INTO #t 
FROM u 

SELECT 
    MIN(id) AS id, [Res_ID], 
    (SELECT MIN([Start]) FROM #t AS sub WHERE sub.[Res_ID] = t.[Res_ID]) AS [Start], 
    (SELECT MAX([End]) FROM #t AS sub WHERE sub.[Res_ID] = t.[Res_ID]) AS [End] 
FROM #t AS t 
GROUP BY [Res_ID] 
ORDER BY [Res_ID]