Wählen Sie Zeilen mit derselben ID, aber null und einem anderen Wert in einer anderen Spalte für diese ID aus

9

Ich möchte nur Zeilen mit einem Wert NULLund einem anderen Wert als NULLfür eine bestimmte Benutzername-Spalte erhalten.

Wenn beide Zeilen für diesen bestimmten Benutzernamen null haben oder beide andere Werte als null haben, sollte dies nicht in der Ausgabe erscheinen. Wenn es mehr als zwei Zeilen für denselben Benutzernamen mit null und einem anderen Wert gibt, sollten sie angezeigt werden.

Unten finden Sie ein Beispiel und eine Ausgabe. Wie geht das mit SQL Query?

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

Ausgabe

+----------+------+
| username | col2 |
+----------+------+
| c        | der  |
| c        | null |
+----------+------+
IT-Forscher
quelle
1
Was ist, wenn es 2 Zeilen mit d, derund 2 mit gibt d, null?
Ypercubeᵀᴹ
1
@ypercube Dann sollten alle 4 Zeilen von d erscheinen
IT-Forscher
1
Wenn es Zeilen mit e, one, e, twound zwei oder mehr mit e, null?
Ypercubeᵀᴹ
1
@ypercube dann sollten alle Zeilen erscheinen.
IT-Forscher

Antworten:

12

Sie sollten in der Lage sein, die bedingte Aggregation zu verwenden, um den Benutzernamen sowohl mit einem Wert in col2als auch mit zu erhalten null.

Ich würde vorschlagen, eine HAVING-Klausel mit den Bedingungen zu verwenden. Die Abfrage wäre ähnlich wie:

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

Siehe SQL Fiddle mit Demo . Diese Abfrage gruppiert Ihre Daten nach jedem Benutzernamen und überprüft dann mithilfe der bedingten Logik, ob col2beide gewünschten Bedingungen erfüllt sind - wobei col2nicht null und col2 null ist.

Sie können dies dann in einer Unterabfrage usw. verwenden, um die Werte usernameund abzurufen 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

Siehe SQL Fiddle mit Demo .

Wenn Sie mehr als eine col2Zeile mit beiden nullund einem anderen Wert haben, müssen Sie die HAVINGKlausel nur geringfügig ändern :

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;

Siehe SQL Fiddle mit Demo

Taryn
quelle
Ihre Anfrage hat einen Punkt verfehlt (eigentlich habe ich in Frage auch nicht klar erwähnt). Wenn es mehr als zwei Zeilen für denselben Benutzernamen mit null und einem anderen Wert gibt, sollten sie angezeigt werden. In Ihrer Anfrage werden sie nicht kommen (zum Beispiel in dieser Geige, wenn es eine andere Zeile mit dem Benutzernamen 'c' und einer Null oder einem Wert gibt.
IT-Forscher
1
@ITresearcher Das ist eine einfache Lösung - Sie müssen die HAVINGKlausel ändern , um zu sein >=1- sqlfiddle.com/#!3/8af72/2
Taryn
Ok. Das ist richtig. Antwort von JGA funktioniert auch.
IT-Forscher
8

Eine andere Lösung:

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

Ausführungsplan

In einer ähnlichen logischen Weise:

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

Ausführungsplan

Noch ein anderer:

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;

Ausführungsplan

Paul White 9
quelle
Gute Antwort. Sogar es hat eine bessere Leistung, weil mein Tisch riesig war.
IT-Forscher
5

Nur ein anderer Weg, es zu tun:

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

Dieser funktioniert auch. SQL Fiddle Demo

Ich erhalte C1 als Gesamtzahl der Zeilen für jeden Benutzernamen, C2 als Gesamtzahl der Nullzeilen für jeden Benutzernamen und vergleiche diese Werte später.

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
JGA
quelle
3

Ich würde die Unterabfrage verwenden, um folgende Benutzernamen auszuwählen:

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

Ich habe es mit diesem versucht ...

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;
ammu
quelle
2
Dies führt zu einer Querverbindung. Wenn für einen Benutzernamen 3 Zeilen mit null col2 und 2 Zeilen mit nicht null col2 vorhanden sind, hat das Endergebnis 6 Zeilen, nicht 5. Und col2ist nicht in der Ausgabe enthalten.
Ypercubeᵀᴹ