Ecco undici opzioni per restituire righe duplicate in Oracle Database quando tali righe hanno una chiave primaria o qualche altra colonna di identificatore univoco e si desidera ignorarla.
Dati campione
Utilizzeremo i seguenti dati per i nostri esempi:
SELECT * FROM Dogs;
Risultato:
DOGID | COGNOME | COGNOME |
---|---|---|
1 | Corteccia | Fabio |
2 | Corteccia | Fabio |
3 | Trama | Jones |
4 | Ruff | Robinson |
5 | Wag | Johnson |
6 | Wag | Johnson |
7 | Wag | Johnson |
Le prime due righe sono duplicate e le ultime tre righe sono duplicate. Le righe duplicate condividono esattamente gli stessi valori in tutte le colonne ad eccezione della loro chiave primaria/colonna ID univoco.
La colonna della chiave primaria garantisce che non vi siano righe duplicate, il che è una buona pratica negli RDBMS, poiché le chiavi primarie aiutano a rafforzare l'integrità dei dati. Ma il fatto che le chiavi primarie contengano valori univoci significa che dobbiamo ignorare quella colonna durante la ricerca di duplicati.
Nella nostra tabella sopra, la colonna della chiave primaria è un numero incrementale e il suo valore non ha significato e non è significativo. Possiamo quindi ignorare i dati di quella colonna durante la ricerca di duplicati.
Opzione 1
Ecco la nostra prima opzione per restituire i duplicati:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
ORDER BY Count DESC;
Risultato:
FIRSTNAME | COGNOME | COUNT |
---|---|---|
Schizzi | Johnson | 3 |
Corteccia | Fabio | 2 |
Ruff | Robinson | 1 |
Woof | Jones | 1 |
Qui abbiamo costruito la nostra query con GROUP BY
clausola in modo che l'output sia raggruppato per le colonne pertinenti. Abbiamo anche usato COUNT()
funzione per restituire il numero di righe identiche. E lo abbiamo ordinato per conteggio in ordine decrescente in modo che i duplicati appaiano per primi.
Il risultato ci dice che ci sono tre righe contenenti Wag Johnson e due righe contenenti Bark Smith. Questi sono duplicati (o triplicati nel caso di Wag Johnson). Le altre due righe non hanno duplicati.
Opzione 2
Possiamo aggiungere il HAVING
clausola al nostro esempio precedente per escludere i non duplicati dall'output:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1
ORDER BY Count DESC;
Risultato:
FIRSTNAME | COGNOME | COUNT |
---|---|---|
Schizzi | Johnson | 3 |
Corteccia | Fabio | 2 |
Opzione 3
Possiamo anche verificare la presenza di duplicati su colonne concatenate. In questo caso utilizziamo il DISTINCT
parola chiave per ottenere valori distinti, quindi utilizza COUNT()
funzione per restituire il conteggio:
SELECT
DISTINCT FirstName || ' ' || LastName AS DogName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName || ' ' || LastName
ORDER BY Count DESC;
Risultato:
DOGNAME | COUNT |
---|---|
Wag Johnson | 3 |
Bark Smith | 2 |
Ruff Robinson | 1 |
Woof Jones | 1 |
Opzione 4
Ogni riga in Oracle ha un rowid
pseudocolonna che restituisce l'indirizzo della riga. Il rowid
è un identificatore univoco per le righe nella tabella e di solito il suo valore identifica in modo univoco una riga nel database (sebbene sia importante notare che righe in tabelle diverse che sono archiviate insieme nello stesso cluster possono avere lo stesso rowid
).
Ad ogni modo, possiamo costruire una query che utilizza il rowid
se vogliamo:
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
);
Risultato:
DOGID | COGNOME | COGNOME |
---|---|---|
2 | Corteccia | Fabio |
6 | Wag | Johnson |
7 | Wag | Johnson |
Potremmo sostituire SELECT *
con DELETE
per eseguire un'operazione di deduplicazione sul tavolo.
Nota che avremmo potuto usare DogId
colonna (la nostra chiave primaria) invece di rowid
se volessimo. Detto questo, il rowid
può essere utile se per qualche motivo non puoi utilizzare la colonna della chiave primaria o se la tabella non ha una chiave primaria.
Opzione 5
Ecco un'altra query che utilizza rowid
:
SELECT * FROM Dogs
WHERE rowid > (
SELECT MIN(rowid) FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
);
Risultato:
DOGID | COGNOME | COGNOME |
---|---|---|
2 | Corteccia | Fabio |
6 | Wag | Johnson |
7 | Wag | Johnson |
Come nell'esempio precedente, potremmo sostituire SELECT *
con DELETE
per eliminare le righe duplicate.
Opzione 6
I due rowid
le opzioni precedenti sono ottime se devi ignorare completamente la chiave primaria nella tua query (o se non hai affatto una colonna di chiave primaria). Tuttavia, come accennato, c'è ancora la possibilità di sostituire rowid
con la colonna della chiave primaria, nel nostro caso il DogId
colonna:
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
);
Risultato:
DOGID | COGNOME | COGNOME |
---|---|---|
2 | Corteccia | Fabio |
6 | Wag | Johnson |
7 | Wag | Johnson |
Opzione 7
Ed ecco l'altra query con rowid
sostituito da DogId
colonna:
SELECT * FROM Dogs
WHERE DogId > (
SELECT MIN(DogId) FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
);
Risultato:
DOGID | COGNOME | COGNOME |
---|---|---|
2 | Corteccia | Fabio |
6 | Wag | Johnson |
7 | Wag | Johnson |
Opzione 8
Un altro modo per trovare i duplicati è utilizzare ROW_NUMBER()
funzione finestra:
SELECT
DogId,
FirstName,
LastName,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS row_num
FROM Dogs;
Risultato:
DOGID | COGNOME | COGNOME | RIGA_NUM |
---|---|---|---|
1 | Corteccia | Fabio | 1 |
2 | Corteccia | Fabio | 2 |
4 | Ruff | Robinson | 1 |
7 | Wag | Johnson | 1 |
5 | Wag | Johnson | 2 |
6 | Wag | Johnson | 3 |
3 | Trama | Jones | 1 |
Usando la PARTITION
La clausola comporta l'aggiunta di una nuova colonna, con un numero di riga che aumenta ogni volta che c'è un duplicato, ma si reimposta di nuovo quando c'è una riga univoca.
In questo caso non raggruppiamo i risultati, il che significa che possiamo vedere ogni riga duplicata, inclusa la relativa colonna dell'identificatore univoco.
Opzione 9
Possiamo anche utilizzare l'esempio precedente come espressione di tabella comune in una query più ampia:
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;
Risultato:
DOGID | COGNOME | COGNOME | RIGA_NUM |
---|---|---|---|
2 | Corteccia | Fabio | 2 |
5 | Wag | Johnson | 2 |
6 | Wag | Johnson | 3 |
Tale query esclude i non duplicati dall'output ed esclude una riga di ogni duplicato dall'output.
Opzione 10
Ecco un altro modo per ottenere lo stesso output dell'esempio precedente:
SELECT * FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
MINUS SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName
);
Risultato:
DOGID | COGNOME | COGNOME |
---|---|---|
2 | Corteccia | Fabio |
6 | Wag | Johnson |
7 | Wag | Johnson |
Questo esempio utilizza MINUS
di Oracle operatore, che restituisce solo righe univoche restituite dalla prima query ma non dalla seconda.
Il MINUS
l'operatore è simile a EXCEPT
operatore in altri DBMS, come SQL Server, MariaDB, PostgreSQL e SQLite.
Opzione 11
Ecco un'altra opzione per selezionare i duplicati dalla nostra tabella:
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
);
Risultato:
DOGID | COGNOME | COGNOME | DOGID | COGNOME | COGNOME |
---|---|---|---|---|---|
2 | Corteccia | Fabio | 1 | Corteccia | Fabio |
7 | Wag | Johnson | 5 | Wag | Johnson |
7 | Wag | Johnson | 6 | Wag | Johnson |