Domanda
Questa query per la creazione di un elenco di candidati duplicati è abbastanza facile:
SELECT Count(*), Can_FName, Can_HPhone, Can_EMail
FROM Can
GROUP BY Can_FName, Can_HPhone, Can_EMail
HAVING Count(*) > 1
Ma se la regola attuale Voglio controllare contro è FName e (HPhone o e-mail) -? Come posso regolare il GROUP BY per lavorare con questo
Sono abbastanza certo che ho intenzione di finire con un UNION SELECT qui (cioè fare FName, HPhone su uno e FName, EMail dall'altra e combinare i risultati) - ma mi piacerebbe sapere se qualcuno conosce un modo più semplice per farlo.
Grazie in anticipo per qualsiasi aiuto.
Scott nel Maine
Soluzione
Nessuna di queste risposte è corretta. Quassnoi di è un approccio decente, ma si noterà un difetto fatale nelle espressioni "qo.id> dup.id" e "di.chainid Il problema fondamentale è una condizione disgiunta con un raggruppamento, che conduce alla possibilità di due record essendo connessi attraverso un intermedio, anche se non sono direttamente riferibile. per esempio, lei ha affermato questi record dovrebbero essere raggruppate: (1) John 555-00-00 john@example.com (2) John 555-00-01 john@example.com (3) John 555-00-01 john-other@example.com Si può vedere che # 1 e # 2 sono facilmente riconoscibili, come lo sono # 2 e # 3, ma chiaramente # 1 e # 3 non sono direttamente facilmente riconoscibili come gruppo. Questo stabilisce che una soluzione ricorsiva o iterativa è l'unica soluzione possibile. Quindi, la ricorsione non è praticabile perché si può facilmente finire in una situazione di loop. Questo è ciò che Quassnoi stava cercando di evitare con i suoi paragoni ID, ma così facendo ha rotto l'algoritmo. Si potrebbe provare a limitare i livelli di ricorsione, ma non si può quindi completare tutti i rapporti, e sarà ancora potenzialmente essere seguito cicli di nuovo su di te, che porta alla dimensione dei dati eccessiva e inefficienza proibitivo. La soluzione migliore è iterativo: Avviare un set di risultati etichettando ciascun ID come ID unico gruppo, e poi girare attraverso il set di risultati e aggiorna, combinando gli ID nello stesso gruppo unico ID come essi corrispondono a condizione disgiuntiva. Ripetere il processo sul set aggiornato ogni volta fino a ulteriori aggiornamenti possono essere effettuati. creerò codice di esempio per questo al più presto.
Altri suggerimenti
Prima posso consigliare nulla, ho bisogno di conoscere la risposta a questa domanda:
name phone email
John 555-00-00 john@example.com
John 555-00-01 john@example.com
John 555-00-01 john-other@example.com
Cosa COUNT(*)
volete per questi dati?
Aggiornamento:
Se si vuole solo sapere che un record ha qualsiasi i duplicati, utilizzare questo:
WITH q AS (
SELECT 1 AS id, 'John' AS name, '555-00-00' AS phone, 'john@example.com' AS email
UNION ALL
SELECT 2 AS id, 'John', '555-00-01', 'john@example.com'
UNION ALL
SELECT 3 AS id, 'John', '555-00-01', 'john-other@example.com'
UNION ALL
SELECT 4 AS id, 'James', '555-00-00', 'james@example.com'
UNION ALL
SELECT 5 AS id, 'James', '555-00-01', 'james-other@example.com'
)
SELECT *
FROM q qo
WHERE EXISTS
(
SELECT NULL
FROM q qi
WHERE qi.id <> qo.id
AND qi.name = qo.name
AND (qi.phone = qo.phone OR qi.email = qo.email)
)
E 'più efficiente, ma non vi dico dove la catena duplicato iniziato.
Questa query selezionare tutte le voci con il campo speciale, chainid
, che indica dove la catena duplicato iniziato.
WITH q AS (
SELECT 1 AS id, 'John' AS name, '555-00-00' AS phone, 'john@example.com' AS email
UNION ALL
SELECT 2 AS id, 'John', '555-00-01', 'john@example.com'
UNION ALL
SELECT 3 AS id, 'John', '555-00-01', 'john-other@example.com'
UNION ALL
SELECT 4 AS id, 'James', '555-00-00', 'james@example.com'
UNION ALL
SELECT 5 AS id, 'James', '555-00-01', 'james-other@example.com'
),
dup AS (
SELECT id AS chainid, id, name, phone, email, 1 as d
FROM q
UNION ALL
SELECT chainid, qo.id, qo.name, qo.phone, qo.email, d + 1
FROM dup
JOIN q qo
ON qo.name = dup.name
AND (qo.phone = dup.phone OR qo.email = dup.email)
AND qo.id > dup.id
),
chains AS
(
SELECT *
FROM dup do
WHERE chainid NOT IN
(
SELECT id
FROM dup di
WHERE di.chainid < do.chainid
)
)
SELECT *
FROM chains
ORDER BY
chainid
GROUP BY non supporta O - è implicitamente ed e deve includere ogni non-aggregatore nella lista di selezione
.I suppone che si abbia anche un intero ID univoco come chiave primaria in questa tabella. Se non lo fai, è una buona idea avere uno, per questo scopo e molti altri.
Trova i duplicati da un self-join:
select
c1.ID
, c1.Can_FName
, c1.Can_HPhone
, c1.Can_Email
, c2.ID
, c2.Can_FName
, c2.Can_HPhone
, c2.Can_Email
from
(
select
min(ID),
Can_FName,
Can_HPhone,
Can_Email
from Can
group by
Can_FName,
Can_HPhone,
Can_Email
) c1
inner join Can c2 on c1.ID < c2.ID
where
c1.Can_FName = c2.Can_FName
and (c1.Can_HPhone = c2.Can_HPhone OR c1.Can_Email = c2.Can_Email)
order by
c1.ID
La query ti dà N-1 righe per ogni combinazioni N duplicati - se si desidera solo un conteggio con ogni combinazione univoca, contare le righe raggruppate dal lato "sinistro":
select count(1) + 1,
, c1.Can_FName
, c1.Can_HPhone
, c1.Can_Email
from
(
select
min(ID),
Can_FName,
Can_HPhone,
Can_Email
from Can
group by
Can_FName,
Can_HPhone,
Can_Email
) c1
inner join Can c2 on c1.ID < c2.ID
where
c1.Can_FName = c2.Can_FName
and (c1.Can_HPhone = c2.Can_HPhone OR c1.Can_Email = c2.Can_Email)
group by
c1.Can_FName
, c1.Can_HPhone
, c1.Can_Email
Certo, questo è più coinvolto di un sindacato -. Ma penso che illustra un buon modo di pensare duplicati
Progetto la trasformazione desiderata prima da una tabella derivata, quindi effettuare l'aggregazione:
SELECT COUNT(*)
, CAN_FName
, Can_HPhoneOrEMail
FROM (
SELECT Can_FName
, ISNULL(Can_HPhone,'') + ISNULL(Can_EMail,'') AS Can_HPhoneOrEMail
FROM Can) AS Can_Transformed
GROUP BY Can_FName, Can_HPhoneOrEMail
HAVING Count(*) > 1
Regolare l'operazione 'O' come desiderato nella lista dei progetti tabella derivata.
So che questa risposta sarà criticato per l'uso della tabella temporanea, ma funzionerà in ogni caso:
-- create temp table to give the table a unique key
create table #tmp(
ID int identity,
can_Fname varchar(200) null, -- real type and len here
can_HPhone varchar(200) null, -- real type and len here
can_Email varchar(200) null, -- real type and len here
)
-- just copy the rows where a duplicate fname exits
-- (better performance specially for a big table)
insert into #tmp
select can_fname,can_hphone,can_email
from Can
where can_fname exists in (select can_fname from Can
group by can_fname having count(*)>1)
-- select the rows that have the same fname and
-- at least the same phone or email
select can_Fname, can_Hphone, can_Email
from #tmp a where exists
(select * from #tmp b where
a.ID<>b.ID and A.can_fname = b.can_fname
and (isnull(a.can_HPhone,'')=isnull(b.can_HPhone,'')
or (isnull(a.can_email,'')=isnull(b.can_email,'') )
Prova questo:
SELECT Can_FName, COUNT(*)
FROM (
SELECT
rank() over(partition by Can_FName order by Can_FName,Can_HPhone) rnk_p,
rank() over(partition by Can_FName order by Can_FName,Can_EMail) rnk_m,
Can_FName
FROM Can
) X
WHERE rnk_p=1 or rnk_m =1
GROUP BY Can_FName
HAVING COUNT(*)>1