Как удалить повторяющиеся строки?
Каков наилучший способ удаления повторяющихся строк из довольно большой таблицы SQL Server
(т.е. 300 000 строк)?
Строки, конечно, не будут совершенными дубликатами из-за существования поля идентичности RowID
.
MyTable
RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null
Ответы
Ответ 1
Предполагая отсутствие нулей, вы GROUP BY
уникальные столбцы и SELECT
MIN (or MAX)
RowId в качестве строки для сохранения. Затем просто удалите все, у которых не было идентификатора строки:
DELETE 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
Если у вас есть идентификатор GUID вместо целого числа, вы можете заменить
MIN(RowId)
с
CONVERT(uniqueidentifier, MIN(CONVERT(char(36), MyGuidColumn)))
Ответ 2
Другой возможный способ сделать это:
;
--Ensure that any immediately preceding statement is terminated with a semicolon above
WITH cte
AS (SELECT ROW_NUMBER() OVER (PARTITION BY Col1, Col2, Col3
ORDER BY ( SELECT 0)) RN
FROM #MyTable)
DELETE FROM cte
WHERE RN > 1;
Я использую ORDER BY (SELECT 0)
выше, поскольку он произволен, какая строка сохраняется в случае привязки.
Чтобы сохранить последний в RowID
порядке, например, вы можете использовать ORDER BY RowID DESC
Планы выполнения
План выполнения для этого часто проще и эффективнее, чем в принятом ответе, поскольку он не требует самостоятельного присоединения.
Однако это не всегда так. Одним из мест, где может быть предпочтительным решение GROUP BY
, является ситуация, при которой агрегат хеша будет выбран по предпочтению агрегату потока.
Решение ROW_NUMBER
всегда будет давать практически тот же план, тогда как стратегия GROUP BY
более гибкая.
Факторы, которые могли бы поддержать хэш-агрегатный подход, были бы
- Нет полезного индекса в столбцах разбиения
- относительно меньшее количество групп с относительно большим количеством дубликатов в каждой группе.
В экстремальных версиях этого второго случая (если в каждом из них очень мало групп со многими дубликатами) можно было бы просто просто вставить строки в новую таблицу, а затем TRUNCATE
-вставить оригинал и скопировать их обратно минимизировать каротаж по сравнению с удалением очень большой доли строк.
Ответ 3
Там есть хорошая статья о удалении дубликатов на сайте поддержки Microsoft. Это довольно консервативно - они делают все по-отдельности, но должны хорошо работать с большими таблицами.
Я использовал self-joins для этого в прошлом, хотя, возможно, это было бы пресечено предложением HAVING:
DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField
AND dupes.secondDupField = fullTable.secondDupField
AND dupes.uniqueField > fullTable.uniqueField
Ответ 4
Следующий запрос полезен для удаления повторяющихся строк. Таблица в этом примере имеет ID
как столбец идентификатора, а столбцы с дублирующимися данными Column1
, Column2
и Column3
.
DELETE FROM TableName
WHERE ID NOT IN (SELECT MAX(ID)
FROM TableName
GROUP BY Column1,
Column2,
Column3
/*Even if ID is not null-able SQL Server treats MAX(ID) as potentially
nullable. Because of semantics of NOT IN (NULL) including the clause
below can simplify the plan*/
HAVING MAX(ID) IS NOT NULL)
Следующий script показывает использование GROUP BY
, HAVING
, ORDER BY
в одном запросе и возвращает результаты с повторяющимся столбцом и его счетчиком.
SELECT YourColumnName,
COUNT(*) TotalCount
FROM YourTableName
GROUP BY YourColumnName
HAVING COUNT(*) > 1
ORDER BY COUNT(*) DESC
Ответ 5
delete t1
from table t1, table t2
where t1.columnA = t2.columnA
and t1.rowid>t2.rowid
Postgres:
delete
from table t1
using table t2
where t1.columnA = t2.columnA
and t1.rowid > t2.rowid
Ответ 6
DELETE LU
FROM (SELECT *,
Row_number()
OVER (
partition BY col1, col1, col3
ORDER BY rowid DESC) [Row]
FROM mytable) LU
WHERE [row] > 1
Ответ 7
Это приведет к удалению повторяющихся строк, кроме первой строки
DELETE
FROM
Mytable
WHERE
RowID NOT IN (
SELECT
MIN(RowID)
FROM
Mytable
GROUP BY
Col1,
Col2,
Col3
)
Обратитесь (http://www.codeproject.com/Articles/157977/Remove-Duplicate-Rows-from-a-Table-in-SQL-Server)
Ответ 8
Я бы предпочел CTE для удаления повторяющихся строк из таблицы sql server
настоятельно рекомендую следовать этой статье :: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/
сохраняя оригинальные
WITH CTE AS
(
SELECT *,ROW_NUMBER() OVER (PARTITION BY col1,col2,col3 ORDER BY col1,col2,col3) AS RN
FROM MyTable
)
DELETE FROM CTE WHERE RN<>1
без сохранения оригинала
WITH CTE AS
(SELECT *,R=RANK() OVER (ORDER BY col1,col2,col3)
FROM MyTable)
DELETE CTE
WHERE R IN (SELECT R FROM CTE GROUP BY R HAVING COUNT(*)>1)
Ответ 9
Быстрая и грязная для удаления точных дублированных строк (для небольших таблиц):
select distinct * into t2 from t1;
delete from t1;
insert into t1 select * from t2;
drop table t2;
Ответ 10
Я предпочитаю, чтобы подзапрос\имел count (*) > 1 решение для внутреннего соединения, потому что мне было легче его читать, и было очень легко превратиться в оператор SELECT, чтобы проверить, что будет удалено до его запуска.
--DELETE FROM table1
--WHERE id IN (
SELECT MIN(id) FROM table1
GROUP BY col1, col2, col3
-- could add a WHERE clause here to further filter
HAVING count(*) > 1
--)
Ответ 11
SELECT DISTINCT *
INTO tempdb.dbo.tmpTable
FROM myTable
TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable
Ответ 12
Чтобы получить повторяющиеся строки:
SELECT
name, email, COUNT(*)
FROM
users
GROUP BY
name, email
HAVING COUNT(*) > 1
Чтобы удалить повторяющиеся строки:
DELETE users
WHERE rowid NOT IN
SELECT MIN(rowid)
FROM users
GROUP BY name, email);
Ответ 13
Еще одно простое решение можно найти на ссылке, вставленной здесь. Это легко понять и кажется эффективным для большинства подобных проблем. Это для SQL Server, но используемая концепция более чем приемлема.
Вот соответствующие части со страницы:
Рассмотрим эти данные:
EMPLOYEE_ID ATTENDANCE_DATE
A001 2011-01-01
A001 2011-01-01
A002 2011-01-01
A002 2011-01-01
A002 2011-01-01
A003 2011-01-01
Итак, как мы можем удалить эти повторяющиеся данные?
Сначала вставьте столбец идентификатора в эту таблицу, используя следующий код:
ALTER TABLE dbo.ATTENDANCE ADD AUTOID INT IDENTITY(1,1)
Для его устранения воспользуйтесь следующим кодом:
DELETE FROM dbo.ATTENDANCE WHERE AUTOID NOT IN (SELECT MIN(AUTOID) _
FROM dbo.ATTENDANCE GROUP BY EMPLOYEE_ID,ATTENDANCE_DATE)
Ответ 14
Я думал, что поделюсь своим решением, поскольку он работает в особых обстоятельствах.
В моем случае таблица с повторяющимися значениями не имела внешнего ключа (потому что значения дублировались из другого db).
begin transaction
-- create temp table with identical structure as source table
Select * Into #temp From tableName Where 1 = 2
-- insert distinct values into temp
insert into #temp
select distinct *
from tableName
-- delete from source
delete from tableName
-- insert into source from temp
insert into tableName
select *
from #temp
rollback transaction
-- if this works, change rollback to commit and execute again to keep you changes!!
PS: при работе над такими вещами я всегда использую транзакцию, это не только гарантирует, что все выполняется в целом, но и позволяет мне протестировать, не рискуя чем-либо. Но, конечно же, вы должны сделать резервную копию, чтобы быть уверенным...
Ответ 15
Использование CTE. Идея состоит в том, чтобы присоединиться к одному или нескольким столбцам, которые формируют повторяющуюся запись, а затем удалять все, что вам нравится:
;with cte as (
select
min(PrimaryKey) as PrimaryKey
UniqueColumn1,
UniqueColumn2
from dbo.DuplicatesTable
group by
UniqueColumn1, UniqueColumn1
having count(*) > 1
)
delete d
from dbo.DuplicatesTable d
inner join cte on
d.PrimaryKey > cte.PrimaryKey and
d.UniqueColumn1 = cte.UniqueColumn1 and
d.UniqueColumn2 = cte.UniqueColumn2;
Ответ 16
Этот запрос показал очень хорошую производительность для меня:
DELETE tbl
FROM
MyTable tbl
WHERE
EXISTS (
SELECT
*
FROM
MyTable tbl2
WHERE
tbl2.SameValue = tbl.SameValue
AND tbl.IdUniqueValue < tbl2.IdUniqueValue
)
он удалил 1M строк чуть больше 30 секунд из таблицы 2M (50% дубликатов)
Ответ 17
Вот еще одна хорошая статья о удалении дубликатов.
В нем обсуждается, почему его сложно: "SQL основан на реляционной алгебре, а дубликаты не могут встречаться в реляционной алгебре, потому что дубликаты не допускаются в наборе".
Решение таблицы temp и два примера mysql.
В будущем вы собираетесь предотвратить его на уровне базы данных или с точки зрения приложения. Я бы предложил уровень базы данных, потому что ваша база данных должна отвечать за поддержание ссылочной целостности, разработчики просто вызовут проблемы;)
Ответ 18
О, конечно. Используйте временную таблицу. Если вы хотите получить одно, не очень-эффективное выражение, которое "работает", вы можете пойти с:
DELETE FROM MyTable WHERE NOT RowID IN
(SELECT
(SELECT TOP 1 RowID FROM MyTable mt2
WHERE mt2.Col1 = mt.Col1
AND mt2.Col2 = mt.Col2
AND mt2.Col3 = mt.Col3)
FROM MyTable mt)
В принципе, для каждой строки таблицы подвыбор находит верхний RowID всех строк, которые точно соответствуют рассматриваемой строке. Таким образом, вы получаете список RowID, которые представляют собой "оригинальные" не дублированные строки.
Ответ 19
У меня была таблица, где мне нужно было сохранить не дублированные строки.
Я не уверен в скорости или эффективности.
DELETE FROM myTable WHERE RowID IN (
SELECT MIN(RowID) AS IDNo FROM myTable
GROUP BY Col1, Col2, Col3
HAVING COUNT(*) = 2 )
Ответ 20
Другим способом является Создать новую таблицу с теми же полями и с уникальным индексом. Затем переместите все данные из старой таблицы в новую таблицу. Автоматически игнорируется SQL SERVER (существует также опция о том, что делать, если будет повторяющееся значение: игнорировать, прерывать или sth) дублировать значения. Таким образом, у нас есть одна и та же таблица без повторяющихся строк. Если вы не хотите уникальный индекс, после передачи данных вы можете отказаться от него.
В частности, для больших таблиц вы можете использовать DTS (пакет SSIS для импорта/экспорта данных), чтобы быстро переносить все данные в новую уникальную индексированную таблицу. Для 7 миллионов строк это занимает всего несколько минут.
Ответ 21
Используйте этот
WITH tblTemp as
(
SELECT ROW_NUMBER() Over(PARTITION BY Name,Department ORDER BY Name)
As RowNumber,* FROM <table_name>
)
DELETE FROM tblTemp where RowNumber >1
Ответ 22
-
Создать новую пустую таблицу с той же структурой
-
Выполните запрос, подобный этому
INSERT INTO tc_category1
SELECT *
FROM tc_category
GROUP BY category_id, application_id
HAVING count(*) > 1
-
Затем выполните этот запрос
INSERT INTO tc_category1
SELECT *
FROM tc_category
GROUP BY category_id, application_id
HAVING count(*) = 1
Ответ 23
Используя нижеприведенный запрос, мы можем удалять повторяющиеся записи на основе одного столбца или нескольких столбцов. ниже запрос удаляется на основе двух столбцов. имя таблицы: testing
и имена столбцов empno,empname
DELETE FROM testing WHERE empno not IN (SELECT empno FROM (SELECT empno, ROW_NUMBER() OVER (PARTITION BY empno ORDER BY empno)
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1)
or empname not in
(select empname from (select empname,row_number() over(PARTITION BY empno ORDER BY empno)
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1)
Ответ 24
Это самый простой способ удалить повторяющуюся запись
DELETE FROM tblemp WHERE id IN
(
SELECT MIN(id) FROM tblemp
GROUP BY title HAVING COUNT(id)>1
)
http://askme.indianyouth.info/details/how-to-dumplicate-record-from-table-in-using-sql-105
Ответ 25
От уровня приложения (к сожалению). Я согласен с тем, что надлежащий способ предотвращения дублирования находится на уровне базы данных с помощью уникального индекса, но в SQL Server 2005 индекс может быть всего лишь 900 байт, а поле varchar (2048) удаляет это.
Я не знаю, насколько хорошо он будет работать, но я думаю, что вы могли бы написать триггер, чтобы обеспечить это, даже если вы не могли сделать это напрямую с индексом. Что-то вроде:
-- given a table stories(story_id int not null primary key, story varchar(max) not null)
CREATE TRIGGER prevent_plagiarism
ON stories
after INSERT, UPDATE
AS
DECLARE @cnt AS INT
SELECT @cnt = Count(*)
FROM stories
INNER JOIN inserted
ON ( stories.story = inserted.story
AND stories.story_id != inserted.story_id )
IF @cnt > 0
BEGIN
RAISERROR('plagiarism detected',16,1)
ROLLBACK TRANSACTION
END
Кроме того, varchar (2048) кажется мне подозрительным (некоторые вещи в жизни - 2048 байт, но это довольно необычно); если это действительно не varchar (max)?
Ответ 26
Я бы упомянул этот подход, а также он может быть полезен и работает на всех серверах SQL:
Довольно часто существует только один - два дубликата, и идентификаторы и количество дубликатов известны. В этом случае:
SET ROWCOUNT 1 -- or set to number of rows to be deleted
delete from myTable where RowId = DuplicatedID
SET ROWCOUNT 0
Ответ 27
DELETE
FROM
table_name T1
WHERE
rowid > (
SELECT
min(rowid)
FROM
table_name T2
WHERE
T1.column_name = T2.column_name
);
Ответ 28
CREATE TABLE car(Id int identity(1,1), PersonId int, CarId int)
INSERT INTO car(PersonId,CarId)
VALUES(1,2),(1,3),(1,2),(2,4)
--SELECT * FROM car
;WITH CTE as(
SELECT ROW_NUMBER() over (PARTITION BY personid,carid order by personid,carid) as rn,Id,PersonID,CarId from car)
DELETE FROM car where Id in(SELECT Id FROM CTE WHERE rn>1)
Ответ 29
DELETE
FROM MyTable
WHERE NOT EXISTS (
SELECT min(RowID)
FROM Mytable
WHERE (SELECT RowID
FROM Mytable
GROUP BY Col1, Col2, Col3
))
);
Ответ 30
Другой способ сделать это: -
DELETE A
FROM TABLE A,
TABLE B
WHERE A.COL1 = B.COL1
AND A.COL2 = B.COL2
AND A.UNIQUEFIELD > B.UNIQUEFIELD