Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Prosedur Tersimpan untuk Menghapus Rekaman Duplikat di Tabel SQL

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.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tindak lanjut #1 pada pencarian wildcard terkemuka

  2. 7 pekerjaan teratas yang menuntut SQL

  3. Cara Memperbaiki ORA-12505, TNS:pendengar saat ini tidak mengetahui SID yang diberikan di deskriptor koneksi

  4. Notasi Chen

  5. Basis Data Relasional