web-dev-qa-db-fra.com

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

Je souhaite obtenir uniquement des lignes ayant une valeur NULL et une autre valeur que NULL pour 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 |
+----------+------+
9
IT researcher

Vous devriez pouvoir utiliser l'agrégation conditionnelle pour obtenir le nom d'utilisateur avec à la fois une valeur dans col2 ainsi que 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 with Demo . Cette requête regroupe vos données par chaque nom d'utilisateur, puis utilise une logique conditionnelle pour vérifier si col2 remplit les deux conditions que vous souhaitez - où col2 n'est pas nul et col2 est nul.

Vous pouvez ensuite l'utiliser dans une sous-requête, etc. pour obtenir les username et col2 valeurs:

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 with Demo .

Si vous en avez plusieurs col2 ligne avec à la fois null et une autre valeur, il vous suffit alors de modifier légèrement la clause HAVING:

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 with Demo

12
Taryn

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

Execution Plan

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

Execution plan

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;

Execution Plan

8
Paul White 9

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 ;
5
ypercubeᵀᴹ

Celui-ci fonctionne aussi. 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
4
JGA

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;
3
Bosko