Database
 sql >> база данни >  >> RDS >> Database

Съхранена процедура за изтриване на дублиращи се записи в SQL таблица

Понякога по време на нашето изпълнение като DBA се натъкваме на поне една таблица, която е заредена с дублиращи се записи. Дори ако таблицата има първичен ключ (в повечето случаи с автоматично нарастване), останалите полета може да имат дублиращи се стойности.

Въпреки това, SQL Server позволява много начини да се отървете от тези дублиращи се записи (напр. използване на CTE, функция SQL Rank, подзаявки с Group By и т.н.).

Спомням си веднъж, по време на интервю, ме попитаха как да изтрия дублиращи се записи в таблица, като оставям само 1 от всеки. По това време не можех да отговоря, но бях много любопитна. След като проучих малко, намерих много възможности за разрешаване на този проблем.

Сега, години по-късно, аз съм тук, за да ви представя съхранена процедура, която има за цел да отговори на въпроса „как да изтрия дублиращи се записи в SQL таблица?“. Всеки DBA може просто да го използва, за да върши малко домакинство, без да се тревожи твърде много.

Създаване на съхранена процедура:Първоначални съображения

Акаунтът, който използвате, трябва да има достатъчно привилегии, за да създаде съхранена процедура в предвидената база данни.

Акаунтът, изпълняващ тази съхранена процедура, трябва да има достатъчно привилегии, за да изпълнява операциите SELECT и DELETE спрямо таблицата на целевата база данни.

Тази съхранена процедура е предназначена за таблиците на базата данни, които нямат дефиниран първичен ключ (нито ограничение UNIQUE). Въпреки това, ако вашата таблица има първичен ключ, съхранената процедура няма да вземе тези полета под внимание. Той ще извърши търсенето и изтриването въз основа на останалите полета (така че го използвайте много внимателно в този случай).

Как да използвате съхранена процедура в SQL

Копирайте и поставете SP T-SQL кода, наличен в тази статия. SP очаква 3 параметъра:

@schemaName – името на схемата на таблицата на базата данни, ако е приложимо. Ако не – използвайте dbo .

@tableName – името на таблицата на базата данни, където се съхраняват дублиращите се стойности.

@displayOnly – ако е зададено на 1 , действителните дублиращи се записи няма да бъдат изтрити , но се показва само вместо това (ако има такива). По подразбиране тази стойност е зададена на 0 което означава, че действителното изтриване ще се случи ако съществуват дубликати.

SQL Server Съхранена процедура Тестове за изпълнение

За да демонстрирам съхранената процедура, създадох две различни таблици – една без първичен ключ и една с първичен ключ. Вмъкнах някои фиктивни записи в тези таблици. Нека проверим какви резултати получавам преди/след изпълнение на съхранената процедура.

SQL таблица с първичен ключ

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

SQL Съхранена процедура Примерни записи

INSERT INTO test VALUES('A','A',1),('A','B',1),('A','C',1),('B','A',2),('B','B',3),('B','C',4)

Изпълнете съхранена процедура само с дисплей

EXEC DBA_DeleteDuplicates @schemaName = 'dbo',@tableName = 'test',@displayOnly = 1

Тъй като колона 1 и колона 2 формират първичния ключ, дубликатите се оценяват спрямо колоните, които не са първичен ключ, в този случай колона 3. Резултатът е правилен.

Изпълнете съхранена процедура без само показване

EXEC DBA_DeleteDuplicates @schemaName = 'dbo',@tableName = 'test',@displayOnly = 0

Дублираните записи са изчезнали.

Въпреки това, трябва да внимавате с този подход, защото първото появяване на записа е това, което ще изреже. Така че, ако по някаква причина имате нужда от много конкретен запис да бъде изтрит, тогава трябва да се заемете с конкретния си случай отделно.

SQL Таблица без първичен ключ

CREATE TABLE [dbo].[duplicates](
	[column1] [varchar](16) NOT NULL,
	[column2] [varchar](16) NOT NULL,
	[column3] [varchar](16) NOT NULL
) ON [PRIMARY]
GO

SQL Съхранена процедура Примерни записи

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')

Изпълнете съхранена процедура само с дисплей

EXEC DBA_DeleteDuplicates @schemaName = 'dbo',@tableName = 'duplicates',@displayOnly = 1

Резултатът е правилен, това са дублиращите се записи в таблицата.

Изпълнете съхранена процедура без само показване

EXEC DBA_DeleteDuplicates @schemaName = 'dbo',@tableName = 'duplicates',@displayOnly = 0

Съхранената процедура работи според очакванията и дубликатите са изчистени успешно.

Специални случаи за тази Съхранена процедура в SQL

Ако схемата или таблицата, които посочвате, не съществуват във вашата база данни, Съхранената процедура ще ви уведоми и скриптът ще прекрати изпълнението си.

Ако оставите името на схемата празно, скриптът ще ви уведоми и ще прекрати изпълнението му.

Ако оставите името на таблицата празно, скриптът ще ви уведоми и ще прекрати изпълнението му.

Ако изпълните съхранената процедура срещу таблица, която няма дубликати и активирате бита @displayOnly , ще получите празен набор от резултати.

Съхранена процедура на SQL Server:Пълен код

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

Заключение

Ако не знаете как да изтриете дублиращи се записи в SQL таблицата, инструменти като този ще ви бъдат полезни. Всеки DBA може да провери дали има таблици на база данни, които нямат първични ключове (нито уникални ограничения) за тях, които могат да натрупат купчина ненужни записи с течение на времето (потенциално загуба на съхранение). Просто включете и пуснете съхранената процедура и сте готови.

Можете да отидете малко по-далеч и да създадете механизъм за известяване, който да ви уведомява, ако има дубликати за конкретна таблица (разбира се, след като сте внедрили малко автоматизация с помощта на този инструмент), което е доста удобно.

Както при всичко, свързано със задачи на DBA, не забравяйте винаги да тествате всичко в среда на пясъчна среда, преди да натиснете спусъка в производството. И когато го направите, уверете се, че имате резервно копие на таблицата, върху която се фокусирате.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Сериализирането изтрива от клъстерирани индекси на Columnstore

  2. Помощна програма за проверка на клъстер, генерираща голям брой xml файлове във файловата система “/u01”.

  3. Работа с JDBC и Spring

  4. Използване на причинно-следствената връзка за разбиране на изпълнението на заявка

  5. Съвпадение на предлагането с търсенето