Terkadang selama menjalankan kami sebagai DBA, kami menemukan setidaknya satu tabel yang dimuat dengan catatan duplikat. Meskipun tabel memiliki Kunci Utama (dalam banyak kasus, penambahan otomatis), bidang lainnya mungkin memiliki nilai duplikat.
Namun, SQL Server memungkinkan banyak cara untuk menyingkirkan rekaman duplikat tersebut (misalnya menggunakan CTE, fungsi SQL Rank, subkueri dengan Group By, dll.).
Saya ingat suatu ketika, saat wawancara, saya ditanya bagaimana cara menghapus duplikat catatan dalam sebuah tabel sementara masing-masing hanya menyisakan 1 catatan. Saat itu, saya tidak bisa menjawab, tetapi saya sangat penasaran. Setelah sedikit meneliti, saya menemukan banyak opsi untuk menyelesaikan masalah ini.
Sekarang, bertahun-tahun kemudian, saya di sini untuk menyajikan kepada Anda Prosedur Tersimpan yang bertujuan untuk menjawab pertanyaan "bagaimana menghapus catatan duplikat di tabel SQL?". DBA mana pun dapat menggunakannya untuk melakukan pembersihan tanpa terlalu khawatir.
Buat Prosedur Tersimpan:Pertimbangan Awal
Akun yang Anda gunakan harus memiliki hak yang cukup untuk membuat Prosedur Tersimpan di database yang dimaksud.
Akun yang menjalankan Prosedur Tersimpan ini harus memiliki hak yang cukup untuk melakukan operasi SELECT dan DELETE terhadap tabel database target.
Prosedur Tersimpan ini ditujukan untuk tabel database yang tidak memiliki Kunci Utama (atau batasan UNIK) yang ditentukan. Namun, jika tabel Anda memiliki Kunci Utama, Prosedur Tersimpan tidak akan mempertimbangkan bidang tersebut. Ini akan melakukan pencarian dan penghapusan berdasarkan sisa bidang (jadi gunakan dengan sangat hati-hati dalam kasus ini).
Cara Menggunakan Prosedur Tersimpan dalam SQL
Salin &tempel Kode SP T-SQL yang tersedia di artikel ini. SP mengharapkan 3 parameter:
@schemaName – nama skema tabel database jika berlaku. Jika tidak – gunakan dbo .
@tableName – nama tabel database tempat nilai duplikat disimpan.
@displayOnly – jika disetel ke 1 , catatan duplikat sebenarnya tidak akan dihapus , tetapi hanya ditampilkan sebagai gantinya (jika ada). Secara default, nilai ini disetel ke 0 artinya penghapusan yang sebenarnya akan terjadi jika ada duplikat.
Prosedur Tersimpan SQL Server Tes Eksekusi
Untuk mendemonstrasikan Stored Procedure, saya telah membuat dua tabel berbeda – satu tanpa Primary Key, dan satu lagi dengan Primary Key. Saya telah memasukkan beberapa catatan dummy ke dalam tabel ini. Mari kita periksa hasil apa yang saya dapatkan sebelum/setelah menjalankan Prosedur Tersimpan.
Tabel SQL dengan Kunci Utama
CREATE TABLE [dbo].[test](
[column1] [varchar](16) NOT NULL,
[column2] [varchar](16) NOT NULL,
[column3] [varchar](16) NOT NULL,
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED
(
[column1] ASC,
[column2] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Prosedur Tersimpan SQL Contoh Catatan
INSERT INTO test VALUES('A','A',1),('A','B',1),('A','C',1),('B','A',2),('B','B',3),('B','C',4)
Jalankan Prosedur Tersimpan dengan Tampilan Saja
EXEC DBA_DeleteDuplicates @schemaName = 'dbo',@tableName = 'test',@displayOnly = 1
Karena column1 dan column2 membentuk Primary Key, duplikat dievaluasi terhadap kolom non-Primary Key, dalam hal ini, column3. Hasilnya benar.
Jalankan Prosedur Tersimpan tanpa Tampilan Saja
EXEC DBA_DeleteDuplicates @schemaName = 'dbo',@tableName = 'test',@displayOnly = 0
Catatan duplikat hilang.
Namun, Anda harus berhati-hati dengan pendekatan ini karena kemunculan pertama dari record adalah yang akan dipotong. Jadi, jika karena alasan apa pun Anda memerlukan catatan yang sangat spesifik untuk dihapus, maka Anda harus menangani kasus khusus Anda secara terpisah.
SQL Tabel tanpa Kunci Utama
CREATE TABLE [dbo].[duplicates](
[column1] [varchar](16) NOT NULL,
[column2] [varchar](16) NOT NULL,
[column3] [varchar](16) NOT NULL
) ON [PRIMARY]
GO
Prosedur Tersimpan SQL Contoh Catatan
INSERT INTO duplicates VALUES
('John','Smith','Y'),
('John','Smith','Y'),
('John','Smith','N'),
('Peter','Parker','N'),
('Bruce','Wayne','Y'),
('Steve','Rogers','Y'),
('Steve','Rogers','Y'),
('Tony','Stark','N')
Jalankan Prosedur Tersimpan dengan Tampilan Saja
EXEC DBA_DeleteDuplicates @schemaName = 'dbo',@tableName = 'duplicates',@displayOnly = 1
Outputnya benar, itu adalah duplikat record dalam tabel.
Jalankan Prosedur Tersimpan tanpa Tampilan Saja
EXEC DBA_DeleteDuplicates @schemaName = 'dbo',@tableName = 'duplicates',@displayOnly = 0
Prosedur Tersimpan telah bekerja seperti yang diharapkan dan duplikat berhasil dibersihkan.
Kasus Khusus untuk Prosedur Tersimpan ini dalam SQL
Jika skema atau tabel yang Anda tentukan tidak ada dalam database Anda, Prosedur Tersimpan akan memberi tahu Anda, dan skrip akan mengakhiri eksekusinya.
Jika Anda membiarkan nama skema kosong, skrip akan memberi tahu Anda dan mengakhiri eksekusinya.
Jika Anda membiarkan nama tabel kosong, skrip akan memberi tahu Anda dan mengakhiri eksekusinya.
Jika Anda menjalankan Prosedur Tersimpan terhadap tabel yang tidak memiliki duplikat dan mengaktifkan @displayOnly bit , Anda akan mendapatkan kumpulan hasil kosong.
Prosedur Tersimpan SQL Server:Kode Lengkap
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author : Alejandro Cobar
-- Create date: 2021-06-01
-- Description: SP to delete duplicate rows in a table
-- =============================================
CREATE PROCEDURE DBA_DeleteDuplicates
@schemaName VARCHAR(128),
@tableName VARCHAR(128),
@displayOnly BIT = 0
AS
BEGIN
SET NOCOUNT ON;
IF LEN(@schemaName) = 0
BEGIN
PRINT 'You must specify the schema of the table!'
RETURN
END
IF LEN(@tableName) = 0
BEGIN
PRINT 'You must specify the name of the table!'
RETURN
END
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = @schemaName AND TABLE_NAME = @tableName)
BEGIN
DECLARE @pkColumnName VARCHAR(128);
DECLARE @columnName VARCHAR(128);
DECLARE @sqlCommand VARCHAR(MAX);
DECLARE @columnsList VARCHAR(MAX);
DECLARE @pkColumnsList VARCHAR(MAX);
DECLARE @pkColumns TABLE(pkColumn VARCHAR(128));
DECLARE @limit INT;
INSERT INTO @pkColumns
SELECT K.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS C
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS K ON C.TABLE_NAME = K.TABLE_NAME AND C.CONSTRAINT_SCHEMA = K.CONSTRAINT_SCHEMA
WHERE C.CONSTRAINT_TYPE = 'PRIMARY KEY'
AND C.CONSTRAINT_SCHEMA = @schemaName AND C.TABLE_NAME = @tableName
IF((SELECT COUNT(*) FROM @pkColumns) > 0)
BEGIN
DECLARE pk_cursor CURSOR FOR
SELECT * FROM @pkColumns
OPEN pk_cursor
FETCH NEXT FROM pk_cursor INTO @pkColumnName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @pkColumnsList = CONCAT(@pkColumnsList,'',@pkColumnName,',')
FETCH NEXT FROM pk_cursor INTO @pkColumnName
END
CLOSE pk_cursor
DEALLOCATE pk_cursor
SET @pkColumnsList = SUBSTRING(@pkColumnsList,1,LEN(@pkColumnsList)-1)
END
DECLARE columns_cursor CURSOR FOR
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @schemaName AND TABLE_NAME = @tableName AND COLUMN_NAME NOT IN (SELECT pkColumn FROM @pkColumns)
ORDER BY ORDINAL_POSITION;
OPEN columns_cursor
FETCH NEXT FROM columns_cursor INTO @columnName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @columnsList = CONCAT(@columnsList,'',@columnName,',')
FETCH NEXT FROM columns_cursor INTO @columnName
END
CLOSE columns_cursor
DEALLOCATE columns_cursor
SET @columnsList = SUBSTRING(@columnsList,1,LEN(@columnsList)-1)
IF((SELECT COUNT(*) FROM @pkColumns) > 0)
BEGIN
IF(CHARINDEX(',',@columnsList) = 0)
SET @limit = LEN(@columnsList)+1
ELSE
SET @limit = CHARINDEX(',',@columnsList)
SET @sqlCommand = CONCAT('WITH CTE (',@columnsList,',DuplicateCount',')
AS (SELECT ',@columnsList,',',
'ROW_NUMBER() OVER(PARTITION BY ',@columnsList,' ',
'ORDER BY ',SUBSTRING(@columnsList,1,@limit-1),') AS DuplicateCount
FROM [',@schemaName,'].[',@tableName,'])
')
IF @displayOnly = 0
SET @sqlCommand = CONCAT(@sqlCommand,'DELETE FROM CTE WHERE DuplicateCount > 1;')
IF @displayOnly = 1
SET @sqlCommand = CONCAT(@sqlCommand,'SELECT ',@columnsList,',MAX(DuplicateCount) AS DuplicateCount FROM CTE WHERE DuplicateCount > 1 GROUP BY ',@columnsList)
END
ELSE
BEGIN
SET @sqlCommand = CONCAT('WITH CTE (',@columnsList,',DuplicateCount',')
AS (SELECT ',@columnsList,',',
'ROW_NUMBER() OVER(PARTITION BY ',@columnsList,' ',
'ORDER BY ',SUBSTRING(@columnsList,1,CHARINDEX(',',@columnsList)-1),') AS DuplicateCount
FROM [',@schemaName,'].[',@tableName,'])
')
IF @displayOnly = 0
SET @sqlCommand = CONCAT(@sqlCommand,'DELETE FROM CTE WHERE DuplicateCount > 1;')
IF @displayOnly = 1
SET @sqlCommand = CONCAT(@sqlCommand,'SELECT * FROM CTE WHERE DuplicateCount > 1;')
END
EXEC (@sqlCommand)
END
ELSE
BEGIN
PRINT 'Table doesn't exist within this database!'
RETURN
END
END
GO
Kesimpulan
Jika Anda tidak tahu cara menghapus catatan duplikat di tabel SQL, alat seperti ini akan membantu Anda. DBA mana pun dapat memeriksa apakah ada tabel database yang tidak memiliki Kunci Utama (atau batasan Unik) untuk mereka, yang mungkin mengakumulasi tumpukan catatan yang tidak perlu dari waktu ke waktu (berpotensi membuang-buang penyimpanan). Cukup pasang dan mainkan Prosedur Tersimpan, dan Anda siap melakukannya.
Anda dapat melangkah lebih jauh dan membangun mekanisme peringatan untuk memberi tahu Anda jika ada duplikat untuk tabel tertentu (tentu saja setelah menerapkan sedikit otomatisasi menggunakan alat ini), yang cukup berguna.
Seperti apa pun yang terkait dengan tugas DBA, pastikan untuk selalu menguji semuanya di lingkungan kotak pasir sebelum menarik pelatuk dalam produksi. Dan ketika Anda melakukannya, pastikan untuk memiliki cadangan tabel yang menjadi fokus Anda.