2010-08-17 18 views
17

Ho righe in una tabella di database Oracle che dovrebbe essere univoca per una combinazione di due campi ma il vincolo univoco non è impostato sulla tabella, quindi ho bisogno di trovare tutte le righe che violano il vincolo utilizzando SQL. Sfortunatamente le mie scarse competenze SQL non sono all'altezza del compito.SQL: come trovare i duplicati in base a due campi?

La mia tabella ha tre colonne rilevanti: entity_id, station_id e obs_year. Per ogni riga, la combinazione di station_id e obs_year dovrebbe essere univoca, e voglio scoprire se ci sono righe che violano questa situazione svuotandole con una query SQL.

Ho provato il seguente SQL (suggerito da this previous question), ma non funziona per me (ho ORA-00918 colonna ambiguo definito):

SELECT 
entity_id, station_id, obs_year 
FROM 
mytable t1 
INNER JOIN (
SELECT entity_id, station_id, obs_year FROM mytable 
GROUP BY entity_id, station_id, obs_year HAVING COUNT(*) > 1) dupes 
ON 
t1.station_id = dupes.station_id AND 
t1.obs_year = dupes.obs_year 

Qualcuno può suggerire quello che sto facendo male e/o come risolvere questo?

risposta

33
SELECT * 
FROM (
     SELECT t.*, ROW_NUMBER() OVER (PARTITION BY station_id, obs_year ORDER BY entity_id) AS rn 
     FROM mytable t 
     ) 
WHERE rn > 1 
+0

Grazie mille per questa risposta. Sfortunatamente, quando eseguo questo, viene visualizzato un messaggio "ORA-00923: FROM keyword not found where expected". –

+0

@James: prova ora – Quassnoi

+0

In mssql in doveva mettere un 'as x' (il nome non ha molta importanza) dietro la parata di FROM() per farlo funzionare. Bella risposta! – Mafii

2

Re-scrittura della query

SELECT 
t1.entity_id, t1.station_id, t1.obs_year 
FROM 
mytable t1 
INNER JOIN (
SELECT entity_id, station_id, obs_year FROM mytable 
GROUP BY entity_id, station_id, obs_year HAVING COUNT(*) > 1) dupes 
ON 
t1.station_id = dupes.station_id AND 
t1.obs_year = dupes.obs_year 

penso che l'errore della colonna ambiguo (ORA-00918) è stato perché eri select colonne ing cui nomi apparsi sia la tavola e la subquery, ma non ha fatto specificare se lo si desidera da dupes o da mytable (con alias come t1).

1

Non è possibile creare una nuova tabella che include il vincolo univoco e quindi copiare i dati riga per riga, ignorando i guasti?

+0

Sì, questa è una buona idea, grazie! BTW Sto cercando di capire come creare il vincolo sulla mia tabella usando le annotazioni nella mia classe di entità (sono uno sviluppatore Java che usa JPA/Hibernate), vedi http://stackoverflow.com/questions/3504477/ how-to-specific-that-a-combination-of-columns-should-be-a-unique-constraint-using –

2

modificare i 3 campi di selezione iniziale per essere

SELECT 
t1.entity_id, t1.station_id, t1.obs_year 
10
SELECT entity_id, station_id, obs_year 
FROM mytable t1 
WHERE EXISTS (SELECT 1 from mytable t2 Where 
     t1.station_id = t2.station_id 
     AND t1.obs_year = t2.obs_year 
     AND t1.RowId <> t2.RowId) 
+0

Sembra che non siamo in grado di farlo su una vista: ORA-01445: impossibile selezionare ROWID da, o campionare, una vista di join senza una tabella conservata con chiave – Thyag

1

È necessario specificare la tabella per le colonne nella select principale. Inoltre, assumendo entity_id è la chiave univoca per mytable ed è irrilevante trovare i duplicati, non dovresti raggruppare su di essa nella subquery dei dupes.

Prova:

SELECT t1.entity_id, t1.station_id, t1.obs_year 
FROM mytable t1 
INNER JOIN (
SELECT station_id, obs_year FROM mytable 
GROUP BY station_id, obs_year HAVING COUNT(*) > 1) dupes 
ON 
t1.station_id = dupes.station_id AND 
t1.obs_year = dupes.obs_year 
+0

Grazie, Contrassegna, per il suggerimento su non utilizzare entity_id nella sottoquery di raggruppamento e per l'esempio illustrativo. –

0
SELECT * 
FROM (
     SELECT t.*, ROW_NUMBER() OVER (PARTITION BY station_id, obs_year ORDER BY entity_id) AS rn 
     FROM mytable t 
     ) 
WHERE rn > 1 

dal Quassnoi è il più efficiente per tabelle di grandi dimensioni. ho avuto questa analisi dei costi:

SELECT a.dist_code, a.book_date, a.book_no 
FROM trn_refil_book a 
WHERE EXISTS (SELECT 1 from trn_refil_book b Where 
     a.dist_code = b.dist_code and a.book_date = b.book_date and a.book_no = b.book_no 
     AND a.RowId <> b.RowId) 
     ; 

ha dato un costo di 1322341

SELECT a.dist_code, a.book_date, a.book_no 
FROM trn_refil_book a 
INNER JOIN (
SELECT b.dist_code, b.book_date, b.book_no FROM trn_refil_book b 
GROUP BY b.dist_code, b.book_date, b.book_no HAVING COUNT(*) > 1) c 
ON 
a.dist_code = c.dist_code and a.book_date = c.book_date and a.book_no = c.book_no 
; 

ha un costo di 1271699

mentre

SELECT dist_code, book_date, book_no 
FROM (
     SELECT t.dist_code, t.book_date, t.book_no, ROW_NUMBER() OVER (PARTITION BY t.book_date, t.book_no 
      ORDER BY t.dist_code) AS rn 
     FROM trn_refil_book t 
     ) p 
WHERE p.rn > 1 
; 

ha dato un costo di 1.021.984

La tabella non è stato indicizzato ....

+0

formatta correttamente la tua risposta. – SSP

0
SELECT entity_id, station_id, obs_year 
    FROM mytable 
GROUP BY entity_id, station_id, obs_year 
HAVING COUNT(*) > 1 

specificare i campi per trovare i duplicati sia sul SELECT e GROUP BY.

Funziona utilizzando GROUP BY per trovare le righe che corrispondono ad altre righe in base alle colonne specificate. Il HAVING COUNT(*) > 1 dice che siamo interessati solo a vedere le righe che si verificano più di 1 volta (e sono quindi duplicate)

+0

Hiya, questo potrebbe risolvere il problema ... ma sarebbe bello se tu potessi fornire una piccola spiegazione su come e perché funziona :) Non dimenticare - ci sono un mucchio di neofiti su Overflow dello Stack, e potrebbero impara una cosa o due dalla tua esperienza - ciò che è ovvio per te potrebbe non essere così per loro. –

+0

Grazie Taryn. Funziona utilizzando GROUP BY per trovare le righe che corrispondono ad altre righe in base alle colonne specificate. HAVING COUNT (*)> 1 dice che siamo interessati solo a vedere le righe che si verificano più di 1 volta (e quindi sono duplicati) – grokster

+0

Ciao, non dirmelo (nei commenti). Conosco SQL, non mi chiedo ... Questo tipo di spiegazione è "parte della tua risposta completa" ... quindi modifica la tua risposta e aggiungila lì. :) –

Problemi correlati