T-SQL: удаление всех повторяющихся строк, но сохранение одного

Возможный дубликат:
SQL - Как удалить повторяющиеся строки?

У меня есть таблица с очень большим количеством строк. Дубликаты не допускаются, но из-за проблемы с созданием строк я знаю, что в этой таблице есть некоторые дубликаты. Мне нужно удалить лишние строки с точки зрения ключевых столбцов. Некоторые другие столбцы могут иметь несколько разные данные, но меня это не волнует. Однако мне все равно нужно держать одну из этих строк. SELECT DISTINCT не будет работать, потому что он работает со всеми столбцами, и мне нужно подавить дубликаты на основе ключевых столбцов.

Как удалить лишние строки, но сохранить их эффективно?

Ответ 1

Вы не сказали, какую версию вы использовали, но в SQL 2005 и выше вы можете использовать общее табличное выражение с OVER Clause. Это выглядит примерно так:

WITH cte AS (
  SELECT[foo], [bar], 
     row_number() OVER(PARTITION BY foo, bar ORDER BY baz) AS [rn]
  FROM TABLE
)
DELETE cte WHERE [rn] > 1

Поиграйте с ним и посмотрите, что вы получите.

(Edit: В попытке быть полезным, кто-то отредактировал предложение ORDER BY в CTE. Чтобы быть ясным, вы можете заказать все, что хотите здесь, это не должно быть один из столбцов, возвращаемых cte На самом деле, общий прецедент здесь состоит в том, что "foo, bar" - это идентификатор группы, а "baz" - это своего рода штамп времени. Чтобы сохранить последнее, вы сделали бы ORDER BY baz desc)

Ответ 2

Пример запроса:

DELETE FROM Table
WHERE ID NOT IN
(
SELECT MIN(ID)
FROM Table
GROUP BY Field1, Field2, Field3, ...
)

Здесь fields - столбец, по которому вы хотите сгруппировать повторяющиеся строки.

Ответ 3

Вот мой поворот на нем, с запущенным примером. Примечание, это будет работать только в ситуации, когда Id является уникальным, и у вас есть повторяющиеся значения в других столбцах.

DECLARE @SampleData AS TABLE (Id int, Duplicate varchar(20))

INSERT INTO @SampleData
SELECT 1, 'ABC' UNION ALL
SELECT 2, 'ABC' UNION ALL
SELECT 3, 'LMN' UNION ALL
SELECT 4, 'XYZ' UNION ALL
SELECT 5, 'XYZ'

DELETE FROM @SampleData WHERE Id IN (
    SELECT Id FROM (
        SELECT 
            Id
            ,ROW_NUMBER() OVER (PARTITION BY [Duplicate] ORDER BY Id) AS [ItemNumber]
            -- Change the partition columns to include the ones that make the row distinct
        FROM 
            @SampleData
    ) a WHERE ItemNumber > 1 -- Keep only the first unique item
)

SELECT * FROM @SampleData

И результаты:

Id          Duplicate
----------- ---------
1           ABC
3           LMN
4           XYZ

Не уверен, почему то, что я думал о первом... определенно, не самый простой способ, но он работает.