Как я могу удалить повторяющиеся строки в таблице - PullRequest
14 голосов
/ 18 сентября 2008

У меня есть таблица, скажем, 3 столбца. Там нет первичного ключа, поэтому могут быть повторяющиеся строки. Мне нужно просто оставить один и удалить остальные. Любая идея, как это сделать Sql Server?

Ответы [ 13 ]

23 голосов
/ 18 сентября 2008

Я бы выбрал DISTINCT строки и выбросил их во временную таблицу, затем удалил исходную таблицу и скопировал обратно данные из temp. РЕДАКТИРОВАТЬ: теперь с фрагментом кода!

INSERT INTO TABLE_2 
SELECT DISTINCT * FROM TABLE_1
GO
DELETE FROM TABLE_1
GO
INSERT INTO TABLE_1
SELECT * FROM TABLE_2
GO
7 голосов
/ 18 сентября 2008

Следующий пример также работает, когда ваш PK является лишь подмножеством всех столбцов таблицы.

(Примечание: мне больше нравится подход с добавлением еще одного столбца суррогатных идентификаторов. Но, возможно, это решение также пригодится.)

Сначала найдите дубликаты строк:

SELECT col1, col2, count(*)
FROM t1
GROUP BY col1, col2
HAVING count(*) > 1

Если их всего несколько, вы можете удалить их вручную:

set rowcount 1
delete from t1
where col1=1 and col2=1

Значение "rowcount" должно быть в n-1 раз больше количества дубликатов. В этом примере есть 2 дубликата, поэтому количество строк равно 1. Если вы получаете несколько повторяющихся строк, вы должны сделать это для каждого уникального первичного ключа.

Если у вас много дубликатов, скопируйте каждый ключ один раз в другую таблицу:

SELECT col1, col2, col3=count(*)
INTO holdkey
FROM t1
GROUP BY col1, col2
HAVING count(*) > 1

Затем скопируйте ключи, но удалите дубликаты.

SELECT DISTINCT t1.*
INTO holddups
FROM t1, holdkey
WHERE t1.col1 = holdkey.col1
AND t1.col2 = holdkey.col2

В ваших ключах теперь есть уникальные ключи. Проверьте, не получили ли вы никакого результата:

SELECT col1, col2, count(*)
FROM holddups
GROUP BY col1, col2

Удалить дубликаты из исходной таблицы:

DELETE t1
FROM t1, holdkey
WHERE t1.col1 = holdkey.col1
AND t1.col2 = holdkey.col2

Вставить исходные строки:

INSERT t1 SELECT * FROM holddups

Кстати, для полноты: в Oracle есть скрытое поле, которое вы можете использовать (rowid):

DELETE FROM our_table
WHERE rowid not in
(SELECT MIN(rowid)
FROM our_table
GROUP BY column1, column2, column3... ;

см .: Сайт знаний Microsoft

7 голосов
/ 18 сентября 2008

Добавьте столбец идентификаторов в качестве суррогатного первичного ключа и используйте его для идентификации двух из трех строк, которые нужно удалить.

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

4 голосов
/ 19 сентября 2008

Это способ сделать это с помощью Common Table Expressions, CTE. Он не содержит циклов, новых столбцов или чего-либо еще и не вызывает срабатывания нежелательных триггеров (из-за удалений + вставок).

Вдохновлен этой статьей .

CREATE TABLE #temp (i INT)

INSERT INTO #temp VALUES (1)
INSERT INTO #temp VALUES (1)
INSERT INTO #temp VALUES (2)
INSERT INTO #temp VALUES (3)
INSERT INTO #temp VALUES (3)
INSERT INTO #temp VALUES (4)

SELECT * FROM #temp

;
WITH [#temp+rowid] AS
(SELECT ROW_NUMBER() OVER (ORDER BY i ASC) AS ROWID, * FROM #temp)
DELETE FROM [#temp+rowid] WHERE rowid IN 
(SELECT MIN(rowid) FROM [#temp+rowid] GROUP BY i HAVING COUNT(*) > 1)

SELECT * FROM #temp

DROP TABLE #temp   
4 голосов
/ 18 сентября 2008

Вот метод, который я использовал , когда я задал этот вопрос -

DELETE MyTable 
FROM MyTable
LEFT OUTER JOIN (
   SELECT MIN(RowId) as RowId, Col1, Col2, Col3 
   FROM MyTable 
   GROUP BY Col1, Col2, Col3
) as KeepRows ON
   MyTable.RowId = KeepRows.RowId
WHERE
   KeepRows.RowId IS NULL
2 голосов
/ 18 сентября 2008

Это сложная ситуация. Не зная вашей конкретной ситуации (размер таблицы и т. Д.), Я думаю, что вам лучше всего добавить столбец идентификаторов, заполнить его, а затем удалить в соответствии с ним. Вы можете удалить столбец позже, но я бы посоветовал вам сохранить его, так как это действительно хорошо иметь в таблице

0 голосов
/ 02 марта 2009

А как насчет этого решения:

Сначала вы выполняете следующий запрос:

  select 'set rowcount ' + convert(varchar,COUNT(*)-1) + ' delete from MyTable where field=''' + field +'''' + ' set rowcount 0'  from mytable group by field having COUNT(*)>1

И тогда вам просто нужно выполнить возвращенный набор результатов

set rowcount 3 delete from Mytable where field='foo' set rowcount 0
....
....
set rowcount 5 delete from Mytable where field='bar' set rowcount 0

Я рассмотрел случай, когда у вас есть только один столбец, но довольно легко адаптировать тот же подход, что и один столбец. Дайте мне знать, если вы хотите, чтобы я отправил код.

0 голосов
/ 19 сентября 2008

Как насчет:

select distinct * into #t from duplicates_tbl

truncate duplicates_tbl

insert duplicates_tbl select * from #t

drop table #t
0 голосов
/ 18 сентября 2008

Вот еще один способ, с тестовыми данными

create table #table1 (colWithDupes1 int, colWithDupes2 int)
insert into #table1
(colWithDupes1, colWithDupes2)
Select 1, 2 union all
Select 1, 2 union all
Select 2, 2 union all
Select 3, 4 union all
Select 3, 4 union all
Select 3, 4 union all
Select 4, 2 union all
Select 4, 2 


select * from #table1

set rowcount 1
select 1

while @@rowcount > 0
delete #table1  where 1 < (select count(*) from #table1 a2 
   where #table1.colWithDupes1 = a2.colWithDupes1
and #table1.colWithDupes2 = a2.colWithDupes2
)

set rowcount 0

select * from #table1
0 голосов
/ 18 сентября 2008

Манрико Корацци - я специализируюсь на Oracle, а не на MS SQL, поэтому вам придется сказать мне, возможно ли это в качестве повышения производительности: -

  1. Оставьте то же самое, что и ваш первый шаг - вставьте отличные значения в TABLE2 из TABLE1.
  2. Drop TABLE1. (Я полагаю, что удаление должно быть быстрее, чем удаление, так же как усечение происходит быстрее, чем удаление).
  3. Переименуйте TABLE2 в TABLE1 (экономит ваше время, поскольку вы переименовываете объект, а не копируете данные из одной таблицы в другую).
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...