Seleziona le righe con lo stesso ID ma null e qualche altro valore in un'altra colonna per quell'id


9

Voglio ottenere solo le righe con un valore NULLe qualche altro valore rispetto NULLa una determinata colonna nome utente.

Se entrambe le righe hanno null per quel particolare nome utente o entrambi hanno alcuni valori diversi da null, non dovrebbe apparire nell'output. Se ci sono più di due righe per lo stesso nome utente con null e qualche altro valore, dovrebbero apparire.

Di seguito è riportato un esempio e un output. Come si può fare usando la query sql?

+----------+-------+
| username | col2  |
+----------+-------+
| a        | abc   |
| a        | ef    |
| b        | null  |
| b        | null  |
| c        | der   |
| c        | null  |
+----------+-------+

produzione

+----------+------+
| username | col2 |
+----------+------+
| c        | der  |
| c        | null |
+----------+------+

1
Cosa succede se ci sono 2 righe con d, dere 2 con d, null?
ypercubeᵀᴹ

1
@ypercube Quindi dovrebbero apparire tutte e 4 le righe di d
Ricercatore IT il

1
Se ci sono righe con e, one, e, twoe 2 o più con e, null?
ypercubeᵀᴹ

1
@ypercube allora dovrebbero apparire tutte le righe.
Ricercatore IT

Risposte:


12

Dovresti essere in grado di utilizzare l'aggregazione condizionale per ottenere il nome utente con un valore col2sia in null.

Suggerirei di utilizzare una clausola HAVING con le condizioni. La query sarebbe simile a:

select username
from yourtable
group by username
having sum(case when col2 is not null then 1 else 0 end) = 1
  and sum(case when col2 is null then 1 else 0 end) = 1

Vedi SQL Fiddle with Demo . Questa query raggruppa i dati in base a ciascun nome utente e quindi utilizza la logica condizionale per verificare se col2soddisfa entrambe le condizioni desiderate: dove col2non è nullo ed col2 è nullo.

È quindi possibile utilizzare questo in una sottoquery, ecc. Per ottenere i valori usernamee col2:

select 
  t.username, 
  t.col2
from yourtable t
inner join
(
  select username
  from yourtable
  group by username
  having sum(case when col2 is not null then 1 else 0 end) = 1
    and sum(case when col2 is null then 1 else 0 end) = 1
) d
  on t.username = d.username

Vedi SQL Fiddle with Demo .

Se hai più di una col2riga con entrambi nulle un altro valore, devi solo modificare HAVINGleggermente la clausola:

select 
  t.username, 
  t.col2
from yourtable t
inner join
(
  select username
  from yourtable
  group by username
  having sum(case when col2 is not null then 1 else 0 end) >= 1
    and sum(case when col2 is null then 1 else 0 end) >= 1
) d
  on t.username = d.username;

Vedi SQL Fiddle with Demo


La tua query ha perso un punto (in realtà non ho ancora menzionato chiaramente in questione). Se ci sono più di due righe per lo stesso nome utente con null e qualche altro valore, dovrebbero apparire. nella tua query non arriveranno (ad esempio in quel violino se c'è un'altra riga con il nome utente 'c' e un valore nullo o un valore.
Ricercatore IT

1
@ITresearcher Questa è una soluzione semplice - devi cambiare la HAVINGclausola per essere >=1- sqlfiddle.com/#!3/8af72/2
Taryn

Ok, è corretto. Anche la risposta di JGA funziona.
Ricercatore IT

8

Un'altra soluzione:

SELECT Y1.*
FROM dbo.yourtable AS Y1
WHERE Y1.username = ANY
(
    SELECT Y2.username 
    FROM dbo.yourtable AS Y2
    WHERE Y2.col2 IS NULL
    INTERSECT
    SELECT Y3.username 
    FROM dbo.yourtable AS Y3
    WHERE Y3.col2 IS NOT NULL
);

Progetto esecutivo

In una vena logica simile:

SELECT Y.* 
FROM dbo.yourtable AS Y
WHERE EXISTS
    (
    SELECT * 
    FROM dbo.yourtable AS Y2 
    WHERE Y2.username = Y.username 
    AND Y2.col2 IS NULL
    )
AND EXISTS
    (
    SELECT * 
    FROM dbo.yourtable AS Y3 
    WHERE Y3.username = Y.username 
    AND Y3.col2 IS NOT NULL
    );

Progetto esecutivo

Ancora un altro:

SELECT
    SQ1.username,
    SQ1.col2
FROM 
(
    SELECT
        Y.username, 
        Y.col2,
        MinCol2 = 
            MIN(CASE WHEN Y.col2 IS NULL THEN -1 ELSE 1 END) 
            OVER (PARTITION BY Y.username), 
        MaxCol2 = 
            MAX(CASE WHEN Y.col2 IS NULL THEN -1 ELSE 1 END) 
            OVER (PARTITION BY Y.username)
    FROM dbo.yourtable AS Y
) AS SQ1
WHERE 
    SQ1.MinCol2 = -SQ1.MaxCol2;

Progetto esecutivo


Bella risposta. Anche se ha prestazioni migliori perché il mio tavolo era enorme.
Ricercatore IT

5

Solo un altro modo per farlo:

; WITH cte AS
  ( SELECT username, col2,
           cnt_all  = COUNT(*) OVER (PARTITION BY username),
           not_null = COUNT(col2) OVER (PARTITION BY username)
    FROM yourtable AS a
  )
SELECT username, col2
FROM cte
WHERE cnt_all > not_null 
  AND not_null > 0 ;

4

Anche questo funziona. Demo di SQL Fiddle

Ottengo C1 come righe totali per ciascun nome utente, C2 come righe null totali per ciascun nome utente e confronto questi valori in un secondo momento.

SELECT username, col2 FROM
(
SELECT *,
(SELECT Count(*) FROM T Where username = T1.username) C1,
(SELECT Count(*) FROM T Where username = T1.username and col2 is null) C2
FROM T T1
) T2
WHERE C2 > 0 And C1 <> C2

3

Vorrei utilizzare la query secondaria per selezionare quei nomi utente come:

select username
from   dbo.yourtable
group by username
having sum(distinct case when col2 is not null then 1 else 2 end) = 3;

-1

Ho provato con questo ...

select a.username from  
(select username ,col2 
   from yourtable
where col2 is null) a,(select username ,col2 
                       from yourtable
                        where col2 is not null) b
where a.username=b.username;

2
Ciò indurrà un cross join. Se per un nome utente, ci sono 3 righe con col2 null e 2 righe con col2 non null, il risultato finale avrà 6 righe, non 5. E col2non sarà nell'output.
ypercubeᵀᴹ
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.