중복 행을 제거하려면 어떻게 해야 합니까?
-
09-06-2019 - |
문제
상당히 큰 데이터에서 중복 행을 제거하는 가장 좋은 방법은 무엇입니까? SQL Server
테이블(예:300,000개 이상의 행)?
물론 행은 다음의 존재로 인해 완벽한 복제가 되지 않습니다. RowID
신원 필드.
마이테이블
RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null
해결책
null이 없다고 가정하면 GROUP BY
고유한 열 및 SELECT
그만큼 MIN (or MAX)
유지할 행인 RowId입니다.그런 다음 행 ID가 없는 모든 항목을 삭제하세요.
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
솔루션이 선호될 수 있는 상황은 다음과 같습니다. 해시 집계 스트림 집계보다 우선적으로 선택됩니다.
그만큼 ROW_NUMBER
솔루션은 항상 거의 동일한 계획을 제공하지만 GROUP BY
전략이 더 유연해졌습니다.
해시 집계 접근 방식을 선호할 수 있는 요소는 다음과 같습니다.
- 분할 열에 유용한 인덱스가 없습니다.
- 각 그룹에 상대적으로 더 많은 중복 항목이 있는 상대적으로 적은 수의 그룹
이 두 번째 경우의 극단적인 버전에서는(각각 중복 항목이 많은 그룹이 거의 없는 경우) 단순히 새 테이블에 보관할 행을 삽입하는 것을 고려할 수도 있습니다. TRUNCATE
- 매우 높은 비율의 행을 삭제하는 것에 비해 로깅을 최소화하기 위해 원본을 복사하고 다시 복사합니다.
에 좋은 글이 있어요 중복 제거 Microsoft 지원 사이트에서.꽤 보수적입니다. 모든 작업을 별도의 단계로 수행해야 하지만 큰 테이블에서는 잘 작동합니다.
나는 과거에 이 작업을 수행하기 위해 셀프 조인을 사용했지만 아마도 HAVING 절을 사용하여 예쁘게 꾸밀 수 있을 것입니다.
DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField
AND dupes.secondDupField = fullTable.secondDupField
AND dupes.uniqueField > fullTable.uniqueField
다음 쿼리는 중복 행을 삭제하는 데 유용합니다.이 예의 테이블에는 ID
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
포스트그레스:
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)
SQL Server 테이블에서 중복 행을 삭제하려면 CTE를 선호합니다.
이 기사를 따르는 것이 좋습니다. ::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)
정확히 중복된 행을 삭제하는 Quick and Dirty(작은 테이블의 경우):
select distinct * into t2 from t1;
delete from t1;
insert into t1 select * from t2;
drop table t2;
나는 내부 조인보다 하위 쿼리\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
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);
SELECT DISTINCT *
INTO tempdb.dbo.tmpTable
FROM myTable
TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable
특별한 상황에서 작동하기 때문에 내 솔루션을 공유하고 싶다고 생각했습니다.제 경우에는 중복 값이 있는 테이블에 외래 키가 없었습니다(값이 다른 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!!
추신:이런 작업을 할 때 저는 항상 트랜잭션을 사용합니다. 이는 모든 것이 전체적으로 실행되도록 보장할 뿐만 아니라 어떤 위험도 감수하지 않고 테스트할 수 있게 해줍니다.하지만 확실하게 하려면 어쨌든 백업을 해야 합니다...
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 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
그렇다면 중복된 데이터를 어떻게 삭제할 수 있습니까?
먼저 다음 코드를 사용하여 해당 테이블에 ID 열을 삽입합니다.
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)
이 쿼리는 나에게 매우 좋은 성능을 보여주었습니다.
DELETE tbl
FROM
MyTable tbl
WHERE
EXISTS (
SELECT
*
FROM
MyTable tbl2
WHERE
tbl2.SameValue = tbl.SameValue
AND tbl.IdUniqueValue < tbl2.IdUniqueValue
)
200만 개의 테이블에서 30초가 조금 넘는 시간에 100만 개의 행을 삭제했습니다(50% 중복).
여기에 또 다른 좋은 기사가 있습니다 중복 제거.
왜 어려운지 설명합니다."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)
기본적으로 테이블의 각 행에 대해 하위 선택은 고려 중인 행과 정확히 일치하는 모든 행의 최상위 RowID를 찾습니다.따라서 중복되지 않은 "원래" 행을 나타내는 RowID 목록이 생성됩니다.
중복되지 않은 행을 보존해야 하는 테이블이 있었습니다.속도나 효율성은 잘 모르겠습니다.
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 패키지)를 사용할 수 있습니다.700만 행의 경우 단 몇 분 밖에 걸리지 않습니다.
이것을 사용하십시오
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 서버에서 작동하므로 언급하겠습니다.종종 1~2개의 중복 항목만 있고 ID와 중복 항목의 개수가 알려져 있습니다.이 경우:
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)
제거하려는 행을 미리 보고 유지할 중복 행을 제어하고 싶습니다.보다 http://developer.azurewebsites.net/2014/09/better-sql-group-by-find-duplicate-data/
with MYCTE as (
SELECT ROW_NUMBER() OVER (
PARTITION BY DuplicateKey1
,DuplicateKey2 -- optional
ORDER BY CreatedAt -- the first row among duplicates will be kept, other rows will be removed
) RN
FROM MyTable
)
DELETE FROM MYCTE
WHERE RN > 1
DELETE
FROM MyTable
WHERE NOT EXISTS (
SELECT min(RowID)
FROM Mytable
WHERE (SELECT RowID
FROM Mytable
GROUP BY Col1, Col2, Col3
))
);