Database
 sql >> Baza danych >  >> RDS >> Database

Przechowywana procedura usuwania zduplikowanych rekordów w tabeli SQL

Czasami podczas naszej pracy jako administratorzy baz danych natrafiamy na co najmniej jedną tabelę załadowaną zduplikowanymi rekordami. Nawet jeśli tabela ma klucz podstawowy (w większości przypadków automatycznie przyrostowy), pozostałe pola mogą mieć zduplikowane wartości.

Jednak SQL Server pozwala na wiele sposobów pozbycia się tych zduplikowanych rekordów (np. za pomocą CTE, funkcji SQL Rank, podzapytań z Group By itp.).

Pamiętam, że podczas wywiadu zapytano mnie, jak usunąć zduplikowane rekordy w tabeli, pozostawiając tylko 1 z każdego. Wtedy nie byłam w stanie odpowiedzieć, ale byłam bardzo ciekawa. Po krótkich poszukiwaniach znalazłem wiele opcji rozwiązania tego problemu.

Teraz, po latach, przedstawię Wam procedurę składowaną, która ma odpowiedzieć na pytanie „jak usunąć zduplikowane rekordy w tabeli SQL?”. Każdy administrator DBA może go po prostu użyć do sprzątania, nie martwiąc się zbytnio.

Utwórz procedurę składowaną:uwagi wstępne

Konto, którego używasz, musi mieć wystarczające uprawnienia, aby utworzyć procedurę składowaną w zamierzonej bazie danych.

Konto wykonujące tę procedurę składowaną musi mieć wystarczające uprawnienia, aby wykonać operacje SELECT i DELETE na docelowej tabeli bazy danych.

Ta procedura składowana jest przeznaczona dla tabel bazy danych, które nie mają zdefiniowanego klucza podstawowego (ani ograniczenia UNIQUE). Jeśli jednak tabela ma klucz podstawowy, procedura składowana nie uwzględni tych pól. Przeprowadzi wyszukiwanie i usuwanie na podstawie pozostałych pól (więc w tym przypadku używaj go bardzo ostrożnie).

Jak korzystać z zapisanej procedury w SQL

Skopiuj i wklej kod SP T-SQL dostępny w tym artykule. SP oczekuje 3 parametrów:

@schemaName – nazwa schematu tabeli bazy danych, jeśli ma zastosowanie. Jeśli nie – użyj dbo .

@tableName – nazwa tabeli bazy danych, w której przechowywane są zduplikowane wartości.

@displayOnly – jeśli ustawione na 1 , faktyczne zduplikowane rekordy nie zostaną usunięte , ale wyświetlane tylko zamiast (jeśli istnieje). Domyślnie ta wartość jest ustawiona na 0 co oznacza, że ​​rzeczywiste usunięcie nastąpi jeśli istnieją duplikaty.

Procedura składowana serwera SQL Testy wykonania

Aby zademonstrować procedurę składowaną, stworzyłem dwie różne tabele – jedną bez klucza podstawowego, a drugą z kluczem podstawowym. Do tych tabel wstawiłem kilka fikcyjnych rekordów. Sprawdźmy, jakie wyniki otrzymam przed/po wykonaniu procedury zapisanej.

Tabela SQL z kluczem podstawowym

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

Procedura składowana SQL Rekordy przykładowe

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

Wykonaj zapisaną procedurę tylko z wyświetlaniem

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

Ponieważ column1 i column2 tworzą klucz podstawowy, duplikaty są oceniane względem kolumn innych niż klucz podstawowy, w tym przypadku kolumny 3. Wynik jest prawidłowy.

Wykonaj procedurę przechowywaną bez wyświetlania tylko

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

Zduplikowane rekordy zniknęły.

Musisz jednak uważać z tym podejściem, ponieważ pierwsze wystąpienie rekordu jest tym, które ucina. Tak więc, jeśli z jakiegokolwiek powodu potrzebujesz bardzo konkretnego rekordu do usunięcia, musisz osobno zająć się swoją konkretną sprawą.

SQL Tabela bez klucza podstawowego

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

Procedura składowana SQL Przykładowe rekordy

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

Wykonaj zapisaną procedurę tylko z wyświetlaniem

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

Wynik jest prawidłowy, to są zduplikowane rekordy w tabeli.

Wykonaj procedurę przechowywaną bez wyświetlania tylko

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

Procedura przechowywana działała zgodnie z oczekiwaniami, a duplikaty zostały pomyślnie wyczyszczone.

Przypadki specjalne dla tej procedury składowanej w SQL

Jeśli określony schemat lub tabela nie istnieje w Twojej bazie danych, procedura składowana powiadomi Cię o tym, a skrypt zakończy wykonywanie.

Jeśli pozostawisz nazwę schematu pustą, skrypt powiadomi Cię o tym i zakończy jego wykonywanie.

Jeśli pozostawisz nazwę tabeli pustą, skrypt powiadomi Cię o tym i zakończy jej wykonywanie.

Jeśli wykonasz procedurę składowaną na tabeli, która nie ma żadnych duplikatów, i aktywujesz bit @displayOnly , otrzymasz pusty zestaw wyników.

Procedura przechowywana w programie SQL Server:kompletny kod

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

Wniosek

Jeśli nie wiesz, jak usunąć zduplikowane rekordy w tabeli SQL, to takie narzędzia będą dla Ciebie pomocne. Każdy administrator baz danych może sprawdzić, czy istnieją tabele bazy danych, które nie mają dla nich kluczy podstawowych (ani ograniczeń unikatowości), co może z czasem gromadzić stos niepotrzebnych rekordów (potencjalnie marnując pamięć). Wystarczy podłączyć i uruchomić procedurę przechowywaną i gotowe.

Możesz pójść trochę dalej i zbudować mechanizm ostrzegania, który będzie powiadamiał Cię, jeśli istnieją duplikaty dla określonej tabeli (oczywiście po wdrożeniu odrobiny automatyzacji za pomocą tego narzędzia), co jest bardzo przydatne.

Podobnie jak w przypadku wszystkiego, co dotyczy zadań DBA, upewnij się, że zawsze testujesz wszystko w środowisku piaskownicy przed naciśnięciem spustu w środowisku produkcyjnym. A kiedy to zrobisz, upewnij się, że masz kopię zapasową stołu, na którym się koncentrujesz.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak zamienić część ciągu w SQL?

  2. Instalowanie SQL Express

  3. ScaleGrid wśród 100 najlepszych dostawców usług w chmurze

  4. Easysoft udostępnia most ODBC-ODBC dla systemu Windows 10

  5. Ukryj wrażliwe dane w swoich planach realizacji