2015-01-13 36 views
6

Ho una tabella di dipendenti che contiene circa 25 colonne. Al momento ci sono molti duplicati e vorrei provare a sbarazzarmi di alcuni di questi duplicati.Rimuovere i duplicati con meno valori nulli

In primo luogo, voglio trovare i duplicati cercando più record con gli stessi valori in nome, cognome, numero del dipendente, numero dell'azienda e stato.

SELECT 
    firstname,lastname,employeenumber, companynumber, statusflag 
FROM 
    employeemaster 
GROUP BY 
    firstname,lastname,employeenumber,companynumber, statusflag 
HAVING 
    (COUNT(*) > 1) 

Questo mi dà i duplicati ma il mio obiettivo è quello di trovare e mantenere il miglior record singolo ed eliminare gli altri record. Il "miglior singolo record" è definito dal record con il minor numero di valori NULL in tutte le altre colonne. Come posso fare questo?

Sto usando Microsoft SQL Server 2012 MGMT Studio.

ESEMPIO:

enter image description here

Rosso: DELETE Verde: TENERE

NOTA: Ci sono molte più colonne della tabella di Quello che questa tabella mostra.

+0

si può modificare i record del campione di domande e previsto in uscita –

+0

Certo, ho aggiunto un esempio. – user3788671

+2

Un metodo molto grezzo ma potresti sommare la lunghezza del testo in ogni colonna e ordinare in base a quello? Oppure sommare un IsNull()? – Liath

risposta

2

È possibile utilizzare la tabella sys.columns per ottenere un elenco di colonne e creare una query dinamica. Questa query restituirà un valore 'KeepThese' per ogni record che si desidera mantenere in base ai criteri specificati.

-- insert test data 
create table EmployeeMaster 
    (
    Record int identity(1,1), 
    FirstName varchar(50), 
    LastName varchar(50), 
    EmployeeNumber int, 
    CompanyNumber int, 
    StatusFlag int, 
    UserName varchar(50), 
    Branch varchar(50) 
); 
insert into EmployeeMaster 
    (
    FirstName, 
    LastName, 
    EmployeeNumber, 
    CompanyNumber, 
    StatusFlag, 
    UserName, 
    Branch 
) 
    values 
    ('Jake','Jones',1234,1,1,'JJONES','PHX'), 
    ('Jake','Jones',1234,1,1,NULL,'PHX'), 
    ('Jake','Jones',1234,1,1,NULL,NULL), 
    ('Jane','Jones',5678,1,1,'JJONES2',NULL); 

-- get records with most non-null values with dynamic sys.column query 
declare @sql varchar(max) 
select @sql = ' 
    select e.*, 
     row_number() over(partition by 
          e.FirstName, 
          e.LastName, 
          e.EmployeeNumber, 
          e.CompanyNumber, 
          e.StatusFlag 
          order by n.NonNullCnt desc) as KeepThese 
    from EmployeeMaster e 
     cross apply (select count(n.value) as NonNullCnt from (select ' + 
      replace((
       select 'cast(' + c.name + ' as varchar(50)) as value union all select ' 
       from sys.columns c 
       where c.object_id = t.object_id 
       for xml path('') 
       ) + '#',' union all select #','') + ')n)n' 
from sys.tables t 
where t.name = 'EmployeeMaster' 

exec(@sql) 
+0

Incredibile! :) Ma puoi spiegare dove filtra 'count (n.value)' per prendere solo i valori della colonna not-null? Per prima cosa ho pensato che fosse fatto da 'cross' apply, ma sembra essere fatto da' count' prendendo in considerazione solo i valori 'non nulli'. Ho ragione? – pkuderov

+1

Sì, sei corretto. Ogni volta che si conta una colonna, vengono conteggiati solo i valori non nulli: selezionare il conteggio (valore) da (selezionare 1 come unione di valori tutti selezionare null come valore) v –

1

Prova questo.

;WITH cte 
    AS (SELECT Row_number() 
        OVER(
        partition BY firstname, lastname, employeenumber, companynumber, statusflag 
        ORDER BY (SELECT NULL)) rn, 
       firstname, 
       lastname, 
       employeenumber, 
       companynumber, 
       statusflag, 
       username, 
       branch 
     FROM employeemaster), 
    cte1 
    AS (SELECT a.firstname, 
       a.lastname, 
       a.employeenumber, 
       a.companynumber, 
       a.statusflag, 
       Row_number() 
        OVER(
        partition BY a.firstname, a.lastname, a.employeenumber, a.companynumber, a.statusflag 
        ORDER BY (CASE WHEN a.username IS NULL THEN 1 ELSE 0 END +CASE WHEN a.branch IS NULL THEN 1 ELSE 0 END))rn 
         -- add the remaining columns in case statement 
     FROM cte a 
       JOIN employeemaster b 
        ON a.firstname = b.firstname 
        AND a.lastname = b.lastname 
        AND a.employeenumber = b.employeenumber 
        AND a.companynumbe = b.companynumber 
        AND a.statusflag = b.statusflag) 
SELECT * 
FROM cte1 
WHERE rn = 1 
1

Test con MySQL e utilizzo NULL String concat per trovare il record migliore. Perché LENGTH (NULL || 'data') è 0. Solo se tutta la colonna non NULL esiste una certa lunghezza. Forse questo non è perfetto.

create table EmployeeMaster 
    (
    Record int auto_increment, 
    FirstName varchar(50), 
    LastName varchar(50), 
    EmployeeNumber int, 
    CompanyNumber int, 
    StatusFlag int, 
    UserName varchar(50), 
    Branch varchar(50), 

    PRIMARY KEY(record) 
); 
INSERT INTO EmployeeMaster 
    (
    FirstName, LastName, EmployeeNumber, CompanyNumber, StatusFlag, UserName, Branch 
) VALUES ('Jake', 'Jones', 1234, 1, 1, 'JJONES', 'PHX'), ('Jake', 'Jones', 1234, 1, 1, NULL, 'PHX'), ('Jake', 'Jones', 1234, 1, 1, NULL, NULL), ('Jane', 'Jones', 5678, 1, 1, 'JJONES2', NULL); 

La mia idea interrogazione si presenta così

SELECT e.* 
    FROM employeemaster e 
    JOIN (SELECT firstname, 
       lastname, 
       employeenumber, 
       companynumber, 
       statusflag, 
       MAX(LENGTH (username || branch)) data_quality 
      FROM employeemaster 
     GROUP BY firstname, lastname, employeenumber, companynumber, statusflag 
     HAVING count(*) > 1 
     ) g 
    ON LENGTH (username || branch) = g.data_quality 
Problemi correlati