سؤال

ما هي أفضل طريقة لإزالة الصفوف المكررة من حجم كبير إلى حد ما SQL Server الجدول (أي300000+ صف)؟

الصفوف، بطبيعة الحال، لن تكون مكررة تماما بسبب وجود RowID مجال الهوية.

طاولتي

RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null
هل كانت مفيدة؟

المحلول

على افتراض عدم وجود القيم الخالية، أنت 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

خطط التنفيذ

غالبًا ما تكون خطة التنفيذ لهذا أبسط وأكثر كفاءة من تلك الموجودة في الإجابة المقبولة لأنها لا تتطلب الانضمام الذاتي.

Execution Plans

ولكن هذا ليس هو الحال دائما.مكان واحد حيث GROUP BY قد يكون الحل المفضل هو الحالات التي يكون فيها أ مجموع التجزئة سيتم اختياره بدلاً من تجميع الدفق.

ال ROW_NUMBER سيعطي الحل دائمًا نفس الخطة تقريبًا بينما GROUP BY الإستراتيجية أكثر مرونة.

Execution Plans

العوامل التي قد تفضل نهج التجزئة الإجمالية ستكون

  • لا يوجد فهرس مفيد على أعمدة التقسيم
  • مجموعات أقل نسبيًا مع تكرارات أكثر نسبيًا في كل مجموعة

في الإصدارات المتطرفة من هذه الحالة الثانية (إذا كان هناك عدد قليل جدًا من المجموعات التي تحتوي على العديد من التكرارات في كل منها) يمكن للمرء أيضًا التفكير ببساطة في إدراج الصفوف للاحتفاظ بها في جدول جديد بعد ذلك 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 كعمود هوية والأعمدة التي تحتوي على بيانات مكررة 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)

أفضل CTE لحذف الصفوف المكررة من جدول خادم SQL

أنصح بشدة بمتابعة هذا المقال ::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;

أفضّل حل الاستعلام الفرعي\وجود العد(*) > 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

اعتقدت أنني سأشارك الحل الخاص بي لأنه يعمل في ظل ظروف خاصة.في حالتي، لم يكن الجدول الذي يحتوي على قيم مكررة يحتوي على مفتاح خارجي (لأن القيم كانت مكررة من قاعدة بيانات أخرى).

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

فكيف يمكننا حذف تلك البيانات المكررة؟

أولاً، قم بإدراج عمود هوية في هذا الجدول باستخدام الكود التالي:

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
    )

تم حذف مليون صف في ما يزيد قليلاً عن 30 ثانية من جدول مكون من 2 مليون (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)

بشكل أساسي، بالنسبة لكل صف في الجدول، يبحث التحديد الفرعي عن معرف الصف العلوي لجميع الصفوف التي تشبه تمامًا الصف قيد النظر.وبذلك ينتهي بك الأمر بقائمة من معرفات الصف التي تمثل الصفوف "الأصلية" غير المكررة.

كان لدي جدول حيث كنت بحاجة للحفاظ على الصفوف غير المكررة.لست متأكدا من السرعة أو الكفاءة.

DELETE FROM myTable WHERE RowID IN (
  SELECT MIN(RowID) AS IDNo FROM myTable
  GROUP BY Col1, Col2, Col3
  HAVING COUNT(*) = 2 )

والطريقة الأخرى هي إنشاء جديد الجدول مع نفس الحقول و مع مؤشر فريد.ثم نقل كافة البيانات من الجدول القديم إلى الجدول الجديد.تجاهل SQL SERVER تلقائيًا (يوجد أيضًا خيار حول ما يجب فعله إذا كانت هناك قيمة مكررة:تجاهل أو مقاطعة أو شيء) القيم المكررة.إذن لدينا نفس الجدول بدون صفوف مكررة. إذا كنت لا تريد فهرسًا فريدًا، فيمكنك إسقاطه بعد نقل البيانات.

خصوصاً لطاولات أكبر يمكنك استخدام 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)
  1. قم بإنشاء جدول فارغ جديد بنفس البنية

  2. تنفيذ الاستعلام مثل هذا

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) > 1
    
  3. ثم قم بتنفيذ هذا الاستعلام

    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)

أريد معاينة الصفوف التي أنت على وشك إزالتها والتحكم في الصفوف المكررة التي تريد الاحتفاظ بها.يرى 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
                     ))
               );
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top