Как удалить повторяющиеся строки?
каков наилучший способ удалить дубликаты строк из довольно большого 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
30 ответов
предполагая, что нет нулей, вы 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)))
другой возможный способ сделать это -
;
--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
решение может быть предпочтительным в ситуациях, когда хэш-агрегирования будет выбран в предпочтении к агрегату потока.
The ROW_NUMBER
решение всегда будет давать в значительной степени тот же план, тогда как GROUP BY
стратегия более гибкая.
факторы, которые могут способствовать хэш совокупный подход был бы
- нет полезного индекса на столбцах секционирования
- относительно меньше групп с относительно большим количеством дубликатов в каждой группе
в крайних версиях этого второго случая (если есть очень мало групп со многими дубликатами в каждом) можно также рассмотреть возможность просто вставить строки, чтобы сохранить в новую таблицу, то TRUNCATE
- ing оригинал и копирование их обратно, чтобы минимизировать ведение журнала по сравнению с удалением очень высокого пропорция рядов.
есть хорошая статья о удаление дубликатов на сайте поддержки 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
следующий запрос используется для удаления повторяющихся строк. Таблица в этом примере 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)
следующий скрипт показывает использование GROUP BY
, HAVING
, ORDER BY
в одном запросе и возвращает результаты с повторяющимся столбцом и его количеством.
SELECT YourColumnName,
COUNT(*) TotalCount
FROM YourTableName
GROUP BY YourColumnName
HAVING COUNT(*) > 1
ORDER BY COUNT(*) DESC
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
DELETE LU
FROM (SELECT *,
Row_number()
OVER (
partition BY col1, col1, col3
ORDER BY rowid DESC) [Row]
FROM mytable) LU
WHERE [row] > 1
это удалит повторяющиеся строки, кроме первой строки
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)
Я бы предпочел 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)
быстрый и грязный, чтобы удалить точные повторяющиеся строки (для небольших таблиц):
select distinct * into t2 from t1;
delete from t1;
insert into t1 select * from t2;
drop table t2;
Я предпочитаю решение subquery\having 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
--)
SELECT DISTINCT *
INTO tempdb.dbo.tmpTable
FROM myTable
TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable
Чтобы Получить Повторяющиеся Строки:
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);
еще одно простое решение можно найти по ссылке, вставленной здесь. Это легко понять и кажется эффективным для большинства подобных проблем. Это для 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)
Я думал, что поделюсь своим решением, так как оно работает при особых обстоятельствах. В моем случае таблица с повторяющимися значениями не имеют внешнего ключа (потому что значения дублируются из другой БД).
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: при работе над такими вещами я всегда использую транзакцию, это не только гарантирует, что все выполняется в целом, но и позволяет мне тестировать, не рискуя ничем. Но, конечно, вы должны взять резервную копию в любом случае, чтобы быть уверенным...
этот запрос показал очень хорошую производительность:
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% дубликатов)
использование 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;
вот еще одна хорошая статья на удаление дубликатов.
он обсуждает, почему его трудно:"SQL основан на реляционной алгебре, и дубликаты не могут происходить в реляционной алгебре, потому что дубликаты не допускаются в наборе."
решение временной таблицы и два примера mysql.
в будущем вы собираетесь предотвратить это на уровне базы данных или с точки зрения приложения. Я бы предложил уровень базы данных, потому что ваша база данных должна отвечать за поддержание ссылочной целостности, разработчики просто вызовет проблем ;)
Да, конечно. Используйте временную таблицу. Если вы хотите одно, не очень эффективное утверждение, которое "работает", вы можете пойти с:
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)
в основном, для каждой строки в таблице суб-select находит верхний RowID всех строк, которые точно такие же, как рассматриваемая строка. Таким образом, вы получаете список RowIDs, которые представляют собой "исходные" неповторяющиеся строки.
У меня была таблица, где мне нужно было сохранить не повторяющиеся строки. Я не уверен в скорости или эффективности.
DELETE FROM myTable WHERE RowID IN (
SELECT MIN(RowID) AS IDNo FROM myTable
GROUP BY Col1, Col2, Col3
HAVING COUNT(*) = 2 )
другой путь создать новый таблица с теми же полями и с уникальным индексом. Тогда перенести все данные из старой таблицы в новую таблицу. Автоматически SQL SERVER игнорирует (есть также опция о том, что делать, если будет дублировать значение: игнорировать, прерывать или sth) дублировать значения. Таким образом, мы имеем одну и ту же таблицу без повторяющихся строк. Если вы не хотите уникальный индекс, после передачи данных вы можете удалить это.
особенно для больших таблиц вы можете использовать DTS (пакет служб SSIS для импорта / экспорта данных) для быстрой передачи всех данных в новую уникально индексированную таблицу. Для 7 миллионов строк это займет всего несколько минут.
использовать
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
С помощью запроса ниже мы можем удалить дубликаты записей на основе одного столбца или нескольких столбцов. ниже запрос удаляется на основе двух столбцов. имя таблицы: 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)
создать новую пустую таблицу с такой же структурой
-
выполнить такой запрос
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
Это самый простой способ, чтобы удалить повторяющиеся записи
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
Я бы упомянул этот подход, а также он может быть полезен и работает во всех SQL-серверах: Довольно часто существует только один - два дубликата, и известны идентификаторы и количество дубликатов. В этом случае:
SET ROWCOUNT 1 -- or set to number of rows to be deleted
delete from myTable where RowId = DuplicatedID
SET ROWCOUNT 0
С уровня приложения (к сожалению). Я согласен с тем, что правильный способ предотвращения дублирования на уровне базы данных-использование уникального индекса, но в 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)?
DELETE
FROM
table_name T1
WHERE
rowid > (
SELECT
min(rowid)
FROM
table_name T2
WHERE
T1.column_name = T2.column_name
);
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)
DELETE
FROM MyTable
WHERE NOT EXISTS (
SELECT min(RowID)
FROM Mytable
WHERE (SELECT RowID
FROM Mytable
GROUP BY Col1, Col2, Col3
))
);
другой способ сделать это :--
DELETE A
FROM TABLE A,
TABLE B
WHERE A.COL1 = B.COL1
AND A.COL2 = B.COL2
AND A.UNIQUEFIELD > B.UNIQUEFIELD