2012-04-20 38 views
16

Sto lavorando su una query che è abbastanza simile seguente:Il modo più semplice per eliminare NULL in SELECT DISTINCT?

CREATE TABLE #test (a char(1), b char(1)) 

INSERT INTO #test(a,b) VALUES 
('A',NULL), 
('A','B'), 
('B',NULL), 
('B',NULL) 

SELECT DISTINCT a,b FROM #test 

DROP TABLE #test 

Il risultato è, ovviamente,

a b 
------- 
A NULL 
A B 
B NULL 

L'uscita desidero vedere in realtà è:

a b 
------- 
A B 
B NULL 

Cioè, se una colonna ha un valore in alcuni record ma non in altri, voglio buttare via la riga con NULL per quella colonna. Tuttavia, se una colonna ha un valore NULL per tutti i record, voglio conservare quello NULL.

Qual è il modo più semplice/elegante per eseguire questa operazione in una singola query?

Ho la sensazione che questo sarebbe semplice se non fossi esausto in un venerdì pomeriggio.

+0

Può un valore della colonna 'a' avere più di un valore non nullo in' B'? Se è così, ti aspetti di ottenere entrambi i valori? –

+0

Mi dispiace, sono appena tornato a questo oggi. Per tutti i record nel gruppo, ogni colonna è: 100% popolato con un singolo valore, 100% NULL o parzialmente popolato con un singolo valore e parzialmente NULL. Ad esempio: (1, 2, NULL), (1, 2, NULL), (1, NULL, NULL) dovrebbe ridurre a (1, 2, NULL). –

risposta

8

Prova questo:

select distinct * from test 
where b is not null or a in (
    select a from test 
    group by a 
    having max(b) is null) 

è possibile ottenere il violino here.

Nota se si può avere solo un valore non nullo in b, questo può essere semplificato per:

select a, max(b) from test 
group by a 
+1

+1 molto succinta –

+0

Il secondo fa ciò di cui ho bisogno in questo caso. Ogni colonna è interamente NULL, interamente popolata con un singolo valore o parzialmente popolata con un singolo valore e parzialmente NULL. MAX() risolve il problema elegantemente. Grazie. –

0
SELECT DISTINCT t.a, t.b 
FROM #test t 
WHERE b IS NOT NULL 
OR  NOT EXISTS (SELECT 1 FROM #test u WHERE t.a = u.a AND u.b IS NOT NULL) 
ORDER BY t.a, t.b 
0

Questo è un requisito davvero strano. Mi chiedo come ne hai bisogno.

SELECT DISTINCT a, b 
FROM test t 
WHERE NOT (b IS NULL 
      AND EXISTS 
       (SELECT * 
       FROM test ta 
       WHERE ta.a = t.a 
        AND ta.b IS NOT NULL 
       ) 
      ) 
    AND NOT (a IS NULL 
      AND EXISTS 
       (SELECT * 
       FROM test tb 
       WHERE tb.b = t.b 
        AND tb.a IS NOT NULL 
       ) 
      ) 
0

Beh, non mi piace particolarmente questa soluzione, ma sembra il più appropriato per me .Si noti che la sua descrizione di ciò che si vuole suona esattamente come quello che si ottiene con un LEFT JOIN, quindi:

SELECT DISTINCT a.a, b.b 
FROM #test a 
    LEFT JOIN #test b ON a.a = b.a 
     AND b.b IS NOT NULL 
1

Prova questo:

create table test(
x char(1), 
y char(1) 
); 

insert into test(x,y) values 
('a',null), 
('a','b'), 
('b', null), 
('b', null) 

Query:

with has_all_y_null as 
(
    select x 
    from test 
    group by x 
    having sum(case when y is null then 1 end) = count(x) 
) 
select distinct x,y from test 
where 

    (
     -- if a column has a value in some records but not in others, 
     x not in (select x from has_all_y_null) 

     -- I want to throw out the row with NULL 
     and y is not null 
    ) 
    or 
    -- However, if a column has a NULL value for all records, 
    -- I want to preserve that NULL 
    (x in (select x from has_all_y_null)) 

order by x,y 

uscita:

X Y 
A B 
B NULL 

Test in diretta: http://sqlfiddle.com/#!3/259d6/16

EDIT

Vedendo Mosty's answer, ho semplificato il mio codice:

with has_all_y_null as 
(
    select x 
    from test 
    group by x 

    -- having sum(case when y is null then 1 end) = count(x) 
    -- should have thought of this instead of the code above. Mosty's logic is good: 
    having max(y) is null 
) 
select distinct x,y from test 
where 
    y is not null 
    or 
    (x in (select x from has_all_y_null)) 
order by x,y 

io preferisco approccio CTE, ha una più logica auto-documentazione :-)

Puoi metti anche la documentazione sull'approccio non CTE, se sei consapevole di farlo:

select distinct * from test 
where b is not null or a in 
    (-- has all b null 
    select a from test 
    group by a 
    having max(b) is null) 
0
SELECT a,b FROM #test t where b is not null 
union 
SELECT a,b FROM #test t where b is null 
and not exists(select 1 from #test where a=t.a and b is not null) 

Risultato:

a b 
---- ---- 
A B 
B NULL 
1
;WITH CTE 
    AS 
    (
    SELECT DISTINCT * FROM #test 
    ) 
    SELECT a,b 
    FROM CTE   
    ORDER BY CASE WHEN b IS NULL THEN 9999 ELSE b END ; 
+0

Potresti aggiungere una breve descrizione su come funziona la tua soluzione? –

Problemi correlati