Sélectionnez des lignes avec le même identifiant mais nul et une autre valeur dans une autre colonne pour cet identifiant


9

Je souhaite obtenir uniquement des lignes ayant une valeur NULLet une autre valeur que NULLpour une colonne de nom d'utilisateur particulière.

Si les deux lignes ont null pour ce nom d'utilisateur particulier ou si les deux ont des valeurs autres que null, cela ne devrait pas apparaître dans la sortie. S'il y a plus de deux lignes pour le même nom d'utilisateur avec null et une autre valeur, elles devraient apparaître.

Voici un exemple d'échantillon et de sortie. Comment cela peut-il être fait en utilisant la requête SQL?

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

production

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

1
Et s'il y a 2 rangées avec d, deret 2 avec d, null?
ypercubeᵀᴹ

1
@ypercube Ensuite, les 4 lignes de d devraient apparaître
chercheur informatique

1
S'il y a des lignes avec e, one, e, twoet 2 ou plus avec e, null?
ypercubeᵀᴹ

1
@ypercube alors toutes les lignes devraient apparaître.
Chercheur informatique,

Réponses:


12

Vous devriez pouvoir utiliser l'agrégation conditionnelle pour obtenir le nom d'utilisateur avec à la fois une valeur dans col2et null.

Je suggère d'utiliser une clause HAVING avec les conditions. La requête serait similaire à:

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

Voir SQL Fiddle avec démo . Cette requête regroupe vos données par nom d'utilisateur et utilise ensuite une logique conditionnelle pour vérifier si elle col2remplit les deux conditions souhaitées - où col2n'est pas nul et col2 est nul.

Vous pouvez ensuite l'utiliser dans une sous-requête, etc. pour obtenir les valeurs usernameet 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

Voir SQL Fiddle avec démo .

Si vous avez plusieurs col2lignes avec les deux nullet une autre valeur, il vous suffit de modifier HAVINGlégèrement la clause:

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;

Voir SQL Fiddle avec démo


Votre requête a manqué un point (en fait, je n'ai pas clairement mentionné la question aussi). S'il y a plus de deux lignes pour le même nom d'utilisateur avec null et une autre valeur, elles devraient apparaître. dans votre requête, ils ne viendront pas (par exemple dans ce violon s'il y a une autre ligne avec le nom d'utilisateur 'c' et une valeur nulle ou une certaine valeur.
Chercheur informatique

1
@ITresearcher C'est une solution simple - vous devez changer la HAVINGclause pour qu'elle soit >=1- sqlfiddle.com/#!3/8af72/2
Taryn

Ok, c'est correct. La réponse de JGA fonctionne également.
Chercheur informatique,

8

Une autre solution:

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
);

Plan d'exécution

Dans la même veine logique:

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
    );

Plan d'exécution

Encore un autre:

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;

Plan d'exécution


Bonne réponse. Même il a de meilleures performances car ma table était énorme.
Chercheur informatique

5

Juste une autre façon de le faire:

; 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

Celui-ci fonctionne aussi. Démo SQL Fiddle

J'obtiens C1 comme nombre total de lignes pour chaque nom d'utilisateur, C2 comme nombre total de lignes nulles pour chaque nom d'utilisateur et je compare ces valeurs plus tard.

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

J'utiliserais la sous-requête pour sélectionner ces noms d'utilisateur comme:

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

-1

J'ai essayé avec celui-ci ...

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
Cela induira une jointure croisée. Si pour un nom d'utilisateur, il y a 3 lignes avec col2 nul et 2 lignes avec col2 non nul, le résultat final aura 6 lignes, pas 5. Et col2ne sera pas dans la sortie.
ypercubeᵀᴹ
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.