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

È stato utile?

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top