【问题标题】:Got mired in a selective DELETE statement on a single table in t-SQL陷入 t-SQL 中单个表的选择性 DELETE 语句中
【发布时间】:2012-07-20 22:03:14
【问题描述】:

说,我有一张这样的表(SQL Server 2008):

CREATE TABLE tbl (ID INT, dtIn DATETIME2, dtOut DATETIME2, Type INT)

INSERT tbl VALUES
(1, '05:00', '6:00', 1),  -- will be removed
(2, '05:00', '7:00', 1),  -- will be removed
(3, '05:01', '8:00', 1),
(4, '05:00', '8:00', 1),
(5, '05:00', '6:00', 2),  -- will be removed
(6, '05:00', '7:00', 2),
(7, '05:00', '7:00', 3),
(8, '04:00', '7:00', 3)

我需要删除所有具有相同“类型”的记录(如果找到 2 条或更多条),它们的“类型”具有相同的“dtIn”,但具有最大“dtOut”的记录除外。换句话说,上表应该是这样的:

(3, '05:01', '8:00', 1),   -- no matching 'dtIn' for 'type' = 1
(4, '05:00', '8:00', 1),   -- largest 'dtOut' for 'type' = 1
(6, '05:00', '7:00', 2),   -- largest 'dtOut' for 'type' = 2
(7, '05:00', '7:00', 3),   -- no matching 'dtIn' for 'type' = 3
(8, '04:00', '7:00', 3)    -- no matching 'dtIn' for 'type' = 4

您如何甚至从同一个表中选择多个具有相同类型的行。你不能 select * from tbl where type=type.... 无论如何,我会很感激这方面的帮助...

【问题讨论】:

    标签: sql sql-server tsql sql-delete


    【解决方案1】:

    这是选择要删除的行的一种方法:

    SELECT *
    FROM tbl T1
    WHERE EXISTS
    (
        SELECT *
        FROM tbl T2
        WHERE T1.Type = T2.Type
        AND T1.dtIn = T2.dtIn
        AND (
                T1.dtOut < T2.dtOut
                OR (T1.dtOut = T2.dtOut AND T1.id < T2.id)
            )
    )
    

    此查询也可以轻松更改为实际删除行。只需将SELECT * 更改为DELETE T1。但请在实际运行 delete 语句之前测试它是否符合您的要求。

    在线查看:sqlfiddle


    更新

    这是一种使用 ROW_NUMBER 的方法:

    ;WITH T1 AS (
        SELECT id, ROW_NUMBER() OVER (PARTITION BY Type, dtIn
                                      ORDER BY dtOut DESC, ID DESC) AS rn
        FROM tbl
    )
    SELECT * FROM tbl
    WHERE id IN
    (
        SELECT id
        FROM T1
        WHERE rn > 1
    )
    

    在线查看:sqlfiddle

    【讨论】:

    • 听着,当我整个上午都在做这件事的时候,你怎么可能在我发布它的 10 秒内完成某件事???
    • @ahmd0:大量练习!我第一次不得不做这样的事情,我也花了很长时间。 :-)
    • 不,说真的,马克,你是怎么学这些东西的?有没有什么特别的书可以教你这个……因为它不仅仅是一种简单的编程技能。
    • @MarkByers ...嗯,很多练习 + this cv
    • @ahmd0:感谢您指出这一点。我修好了它。使用DELETE T1
    【解决方案2】:

    不是很优雅(+ 条件与马克的答案相同),但它是一种替代方案。

    Fiddle is here

    ;with my_cte(ID)
    as
    (
      SELECT T1.ID
      FROM 
          tbl T1
          JOIN tbl T2 on
            T1.Type = T2.Type
            AND T1.dtIn = T2.dtIn
            AND T1.dtOut < T2.dtOut
      )
    select *
    from tbl t
    where not exists 
                  (
                  select 1 
                  from my_cte c 
                  where t.ID = c.ID
                  ) 
    

    【讨论】:

      【解决方案3】:

      重复行的查询

      我刚刚看到您可以有重复的行。所以:

      DELETE X
      FROM
         (SELECT Row_Number() OVER (PARTITION BY Type, dtIn ORDER BY dtOut DESC) Item, *
         FROM #tbl) X
      WHERE Item > 1
      

      请注意,以上版本不需要连接,因此应该比页面上的所有其他查询执行得更好。

      DELETE T
      FROM #tbl T
         CROSS APPLY (
            SELECT TOP 1 *
            FROM #tbl T2
            WHERE
               T.dtIn = T2.dtIn
               AND T.Type = T2.Type
            ORDER BY T2.dtOut DESC, T2.ID DESC
         ) X
      WHERE T.ID < X.ID
      

      DELETE T
      FROM #tbl T
         INNER JOIN #tbl T2
            ON T.dtIn = T2.dtIn
            AND T.Type = T2.Type
            AND (
               T.dtOut < T2.dtOut
               OR (T.tdOut = T2.dtOut AND T.ID > T2.ID)
            )
      

      查询无重复行

      如果您不能有重复的行(ID 除外),那么它会更简单:

      DELETE T
      FROM #tbl T
      WHERE EXISTS (
         SELECT * FROM #tbl T2
         WHERE
            T.dtIn = T2.dtIn
            AND T.Type = T2.Type
            AND T.dtOut < T2.dtOut
      )
      

      甚至

      DELETE T
      FROM #tbl T
         INNER JOIN #tbl T2
            ON T.dtIn = T2.dtIn
            AND T.Type = T2.Type
            AND T.dtOut < T2.dtOut
      

      【讨论】:

      • 谢谢。是的,重复记录是可能的,所以你的短版本将不起作用。
      • @ahmd0 添加了两个版本
      【解决方案4】:

      我的和@Mark 的有点不同,但我相信它应该会得到相同的结果(只是因为我认为id 是独一无二的):

      DELETE a
      FROM dbo.tbl a
      WHERE id <> (
          SELECT MAX(id)
          from dbo.tbl b
          WHERE a.type = b.type
            AND a.dtIn = b.dtIn
      )
      

      【讨论】:

      • 抱歉,sqlfiddle.com 似乎没有通过您的查询返回正确的结果。纠正它超出了我的知识范围......
      • 嗯。奇怪,现在可以了。抱歉,我不确定为什么它给了我不同的结果……也许是因为我很累。也感谢您的意见!
      • 其实,我刚刚意识到,这段代码假设更高的id 也意味着更高的dtOut,事实可能并非如此。将声明中的 ids 更改为 dtOuts 可能会更安全(如果这符合您的实际情况)。
      • @bhamby:对不起,我不确定我是否关注你。你能更正你的实际SQL吗?另请注意,它应该进行删除而不仅仅是选择。我还发现 Mark 的代码似乎无法处理重复的 dtIn 和 dtOut。阅读我对他上面帖子的评论……叮!回到绘图板。但至少我知道你们现在的逻辑是什么……
      【解决方案5】:
      DECLARE @Table TABLE     
      (  
      ID INT,  
      newno int  
      )   
      
      insert @table  
      SELECT T1.ID,   
      ROW_NUMBER() OVER (PARTITION BY Type, dtIn
                                    ORDER BY dtOut DESC, ID DESC) AS newno  
       FROM tbl T1      
      
       select *  
      from tbl t
      where t.ID IN
                   (
                    select ID from @Table where newno > 1
                    )    
      

      【讨论】:

        猜你喜欢
        • 2012-09-20
        • 2015-02-11
        • 1970-01-01
        • 1970-01-01
        • 2011-04-17
        • 1970-01-01
        • 1970-01-01
        • 2017-05-29
        • 1970-01-01
        相关资源
        最近更新 更多