合并表sql server中多行的记录

ste*_*ner 0 c# sql t-sql sql-server-2005 sql-server-2008

我有一些脏的资源使用记录,t_resourcetable其中看起来像这样

resNo   subres    startdate                        enddate
1        2        2012-01-02 22:03:00.000          2012-01-03 00:00:00.000
1        2        2012-01-03 00:00:00.000          2012-01-04 00:00:00.000
1        2        2012-01-04 00:00:00.000          2012-01-04 16:23:00.000
1        3        2012-01-06 16:23:00.000          2012-01-06 22:23:00.000
2        2        2012-01-04 05:23:00.000          2012-01-06 16:23:00.000

我需要以这种方式合并那些脏行

resNo   subres    startdate                        enddate
1        2        2012-01-02 22:03:00.000          2012-01-04 16:23:00.000
1        3        2012-01-06 16:23:00.000          2012-01-06 22:23:00.000
2        2        2012-01-04 05:23:00.000          2012-01-06 16:23:00.000

这应该更新到同一个表.我有超过40k行,所以不能使用游标.请通过更优化的sql语句帮助我清理它.

提供的解决方案不会遇到类似的情况

resNo   subres    startdate                        enddate
1        2        2012-01-02 22:03:00.000          2012-01-03 00:00:00.000
1        2        2012-01-03 00:00:00.000          2012-01-04 00:00:00.000
1        2        2012-01-04 00:00:00.000          2012-01-04 16:23:00.000
1        2        2012-01-14 10:09:00.000          2012-01-15 00:00:00.000
1        2        2012-01-15 00:00:00.000          2012-01-16 00:00:00.000
1        2        2012-01-16 00:00:00.000          2012-01-16 03:00:00.000
1        3        2012-01-06 16:23:00.000          2012-01-06 22:23:00.000
2        2        2012-01-04 05:23:00.000          2012-01-06 16:23:00.000

我需要以这种方式合并那些脏行

resNo   subres    startdate                        enddate
1        2        2012-01-02 22:03:00.000          2012-01-04 16:23:00.000
1        2        2012-01-14 10:09:00.000          2012-01-16 03:00:00.000
1        3        2012-01-06 16:23:00.000          2012-01-06 22:23:00.000
2        2        2012-01-04 05:23:00.000          2012-01-06 16:23:00.000

请帮我解决这个脏数据问题.

Mar*_*rot 6

MERGE INTO t_resourcetable AS TARGET
USING (
    SELECT
        resNo, subres,
        MIN(startdate) as startdate,
        MAX(enddate) as enddate
    FROM t_resourcetable
    GROUP BY resNo, subres
) AS SOURCE
ON TARGET.resNo = SOURCE.resNo
AND TARGET.subres = SOURCE.subres
AND TARGET.startdate = SOURCE.startdate
-- Set enddate on the first record in the group
WHEN MATCHED THEN
    UPDATE SET TARGET.enddate = SOURCE.enddate
-- Delete the remaining items
WHEN NOT MATCHED BY SOURCE THEN
    DELETE;
Run Code Online (Sandbox Code Playgroud)

编辑:尊重间隔中的间隙:

MERGE INTO t_resourcetable AS TARGET
USING (
    -- Find the first item in each interval group
    SELECT
        resNo, subres, startdate,
        row_number() over (partition by resNo, subres order by startdate) as rn
    FROM t_resourcetable t1
    WHERE NOT EXISTS (
        -- No other intervals that intersect this from behind
        SELECT NULL
        FROM t_resourcetable t2
        WHERE t2.resNo = t1.resNo
        AND t2.subres = t1.subres
        AND t2.startdate < t1.startdate
        AND t2.enddate >= t1.startdate
    )
) AS SOURCE_start
INNER JOIN (
    -- Find the last item in each interval group
    SELECT
        resNo, subres, enddate,
        row_number() over (partition by resNo, subres order by startdate) as rn
    FROM t_resourcetable t1
    WHERE NOT EXISTS (
        -- No other intervals that intersect this from ahead
        SELECT NULL
        FROM t_resourcetable t2
        WHERE t2.resNo = t1.resNo
        AND t2.subres = t1.subres
        AND t2.startdate <= t1.enddate
        AND t2.enddate > t1.enddate
    )
) AS SOURCE_end
    ON SOURCE_start.resNo = SOURCE_end.resNo
    AND SOURCE_start.subres = SOURCE_end.subres
    AND SOURCE_start.rn = SOURCE_end.rn -- Join by row number
ON TARGET.resNo = SOURCE_start.resNo
AND TARGET.subres = SOURCE_start.subres
AND TARGET.startdate = SOURCE_start.startdate
-- Set enddate on the first record in the group
WHEN MATCHED THEN
    UPDATE SET TARGET.enddate = SOURCE_end.enddate
-- Delete the remaining items
WHEN NOT MATCHED BY SOURCE THEN
    DELETE;
Run Code Online (Sandbox Code Playgroud)

结果:

resNo   subres   startdate          enddate
    1        2   2012-01-02 22:03   2012-01-04 16:23
    1        2   2012-01-14 10:09   2012-01-16 03:00
    1        3   2012-01-06 16:23   2012-01-06 22:23
    2        2   2012-01-04 05:23   2012-01-06 16:23
Run Code Online (Sandbox Code Playgroud)

编辑:如果目标表上存在任何并发编辑风险,您可能需要添加HOLDLOCK提示.这样可以防止出现任何主键冲突错误,并且可以略微提高资源效率.(谢谢乔伊):

MERGE INTO t_resourcetable WITH (HOLDLOCK) AS TARGET
...
Run Code Online (Sandbox Code Playgroud)