Ето единадесет опции за връщане на дублиращи се редове в Oracle Database, когато тези редове имат първичен ключ или някаква друга колона с уникален идентификатор и искате да го игнорирате.
Примерни данни
Ще използваме следните данни за нашите примери:
SELECT * FROM Dogs;
Резултат:
ДОГИД | FIRSTNAME | ФАМИЛИЯ |
---|---|---|
1 | Кора | Смит |
2 | Кора | Смит |
3 | Уф | Джоунс |
4 | Ръф | Робинзон |
5 | Размахване | Джонсън |
6 | Размахване | Джонсън |
7 | Размахване | Джонсън |
Първите два реда са дублирани, а последните три реда са дублирани. Дублираните редове споделят абсолютно едни и същи стойности във всички колони с изключение на колоната с първичен ключ/уникален идентификатор.
Колоната с първичен ключ гарантира, че няма дублиращи се редове, което е добра практика в RDBMS, тъй като първичните ключове помагат за налагане на целостта на данните. Но фактът, че първичните ключове съдържат уникални стойности, означава, че трябва да игнорираме тази колона, когато търсим дубликати.
В нашата таблица по-горе колоната с първичен ключ е нарастващо число и стойността й няма значение и не е значима. Следователно можем да игнорираме данните на тази колона, когато търсим дубликати.
Опция 1
Ето първата ни опция за връщане на дубликати:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
ORDER BY Count DESC;
Резултат:
FIRSTNAME | LASTNAME | COUNT |
---|---|---|
Размахване | Джонсън | 3 |
Кора | Смит | 2 |
Ръф | Робинзон | 1 |
Уф | Джоунс | 1 |
Тук изградихме нашата заявка с GROUP BY
клауза, така че изходът да бъде групиран по съответните колони. Използвахме и COUNT()
функция за връщане на броя на еднакви редове. И го подредихме по брой в низходящ ред, така че дубликатите да се появят първи.
Резултатът ни казва, че има три реда, съдържащи Уаг Джонсън и два реда, съдържащи Барк Смит. Това са дубликати (или три екземпляра в случая на Уаг Джонсън). Другите два реда нямат дубликати.
Опция 2
Можем да добавим HAVING
клауза към предишния ни пример за изключване на недублирани от изхода:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1
ORDER BY Count DESC;
Резултат:
FIRSTNAME | LASTNAME | COUNT |
---|---|---|
Размахване | Джонсън | 3 |
Кора | Смит | 2 |
Опция 3
Можем също да проверим за дубликати на свързани колони. В този случай използваме DISTINCT
ключова дума, за да получите различни стойности, след това използвайте COUNT()
функция за връщане на броя:
SELECT
DISTINCT FirstName || ' ' || LastName AS DogName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName || ' ' || LastName
ORDER BY Count DESC;
Резултат:
ИМЕ НА КУЧЕ | COUNT |
---|---|
Уаг Джонсън | 3 |
Барк Смит | 2 |
Ръф Робинсън | 1 |
Уау Джоунс | 1 |
Опция 4
Всеки ред в Oracle има rowid
псевдоколона, която връща адреса на реда. rowid
е уникален идентификатор за редове в таблицата и обикновено неговата стойност уникално идентифицира ред в базата данни (въпреки че е важно да се отбележи, че редовете в различни таблици, които се съхраняват заедно в един и същ клъстер, могат да имат един и същ rowidкод> ).
Както и да е, можем да изградим заявка, която използва rowid
ако искаме:
SELECT * FROM Dogs
WHERE EXISTS (
SELECT 1 FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
AND Dogs.rowid > d2.rowid
);
Резултат:
ДОГИД | FIRSTNAME | LASTNAME |
---|---|---|
2 | Кора | Смит |
6 | Размахване | Джонсън |
7 | Размахване | Джонсън |
Бихме могли да заменим SELECT *
с DELETE
за извършване на операция за премахване на дупки на масата.
Имайте предвид, че можехме да използваме DogId
колона (нашия първичен ключ) вместо rowid
ако искахме. Това каза, rowid
може да бъде полезно, ако по някаква причина не можете да използвате колоната с първичен ключ или ако таблицата няма първичен ключ.
Опция 5
Ето още една заявка, която използва rowid
:
SELECT * FROM Dogs
WHERE rowid > (
SELECT MIN(rowid) FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
);
Резултат:
ДОГИД | FIRSTNAME | LASTNAME |
---|---|---|
2 | Кора | Смит |
6 | Размахване | Джонсън |
7 | Размахване | Джонсън |
Както в предишния пример, бихме могли да заменим SELECT *
с DELETE
за да изтриете дублиращите се редове.
Опция 6
Двата rowid
опциите по-горе са страхотни, ако трябва напълно да игнорирате първичния ключ във вашата заявка (или ако изобщо нямате колона с първичен ключ). Въпреки това, както споменахме, все още има опция за замяна на rowid
с колоната с първичен ключ – в нашия случай DogId
колона:
SELECT * FROM Dogs
WHERE EXISTS (
SELECT 1 FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
AND Dogs.DogId > d2.DogId
);
Резултат:
ДОГИД | FIRSTNAME | LASTNAME |
---|---|---|
2 | Кора | Смит |
6 | Размахване | Джонсън |
7 | Размахване | Джонсън |
Опция 7
А ето и другата заявка с rowid
заменен от DogId
колона:
SELECT * FROM Dogs
WHERE DogId > (
SELECT MIN(DogId) FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
);
Резултат:
ДОГИД | FIRSTNAME | LASTNAME |
---|---|---|
2 | Кора | Смит |
6 | Размахване | Джонсън |
7 | Размахване | Джонсън |
Опция 8
Друг начин за намиране на дубликати е да използвате ROW_NUMBER()
функция на прозореца:
SELECT
DogId,
FirstName,
LastName,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS row_num
FROM Dogs;
Резултат:
ДОГИД | FIRSTNAME | ФАМИЛИЯ | ROW_NUM |
---|---|---|---|
1 | Кора | Смит | 1 |
2 | Кора | Смит | 2 |
4 | Ръф | Робинзон | 1 |
7 | Размахване | Джонсън | 1 |
5 | Размахване | Джонсън | 2 |
6 | Размахване | Джонсън | 3 |
3 | Уф | Джоунс | 1 |
Използване на PARTITION
клауза води до добавяне на нова колона с номер на ред, който се увеличава всеки път, когато има дубликат, но се нулира отново, когато има уникален ред.
В този случай не групираме резултатите, което означава, че можем да видим всеки дублиран ред, включително колоната с уникален идентификатор.
Опция 9
Можем също да използваме предишния пример като общ табличен израз в по-голяма заявка:
WITH cte AS
(
SELECT
DogId,
FirstName,
LastName,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS row_num
FROM Dogs
)
SELECT * FROM cte WHERE row_num <> 1;
Резултат:
ДОГИД | FIRSTNAME | LASTNAME | ROW_NUM |
---|---|---|---|
2 | Кора | Смит | 2 |
5 | Размахване | Джонсън | 2 |
6 | Размахване | Джонсън | 3 |
Тази заявка изключва недубликатите от изхода и изключва един ред от всеки дубликат от изхода.
Опция 10
Ето още един начин да получите същия изход като предишния пример:
SELECT * FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
MINUS SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName
);
Резултат:
ДОГИД | FIRSTNAME | ФАМИЛИЯ |
---|---|---|
2 | Кора | Смит |
6 | Размахване | Джонсън |
7 | Размахване | Джонсън |
Този пример използва MINUS
на Oracle оператор, който връща само уникални редове, върнати от първата заявка, но не и от втората.
MINUS
операторът е подобен на EXCEPT
оператор в други СУБД, като SQL Server, MariaDB, PostgreSQL и SQLite.
Опция 11
Ето още една опция за избор на дубликати от нашата таблица:
SELECT *
FROM Dogs d1, Dogs d2
WHERE d1.FirstName = d2.FirstName
AND d1.LastName = d2.LastName
AND d1.DogId <> d2.DogId
AND d1.DogId = (
SELECT MAX(DogId)
FROM Dogs d3
WHERE d3.FirstName = d1.FirstName
AND d3.LastName = d1.LastName
);
Резултат:
ДОГИД | FIRSTNAME | LASTNAME | КУЧЕ | FIRSTNAME | LASTNAME |
---|---|---|---|---|---|
2 | Кора | Смит | 1 | Кора | Смит |
7 | Размахване | Джонсън | 5 | Размахване | Джонсън |
7 | Размахване | Джонсън | 6 | Размахване | Джонсън |