it-swarm.com.de

NICHT existieren

Betrachten Sie die folgende Tabelle:

CREATE TABLE GapsIslands (ID INT NOT NULL, SeqNo INT NOT NULL);

ALTER TABLE GapsIslands ADD CONSTRAINT pk_GapsIslands PRIMARY KEY (ID, SeqNo);

INSERT INTO GapsIslands
SELECT 1, 1 UNION ALL SELECT 1, 2 UNION ALL SELECT 1, 5 UNION ALL SELECT 1, 6 
UNION ALL SELECT 1, 8 UNION ALL SELECT 1, 9 UNION ALL SELECT 1, 10 UNION ALL SELECT 1, 12 
UNION ALL SELECT 1, 20 UNION ALL SELECT 1, 21 UNION ALL SELECT 1, 25 UNION ALL SELECT 1, 26;  

Ich habe Probleme, die Anwendung der Bedingung NOT EXISTS Im Kontext einer Gaps and Islands-Lösung zu verstehen.

Die folgende Abfrage soll die Ausgangspunkte von 'Inseln' finden:

SELECT ID, SeqNo, ROW_NUMBER() OVER (ORDER BY SeqNo) AS RowNum
FROM GapsIslands AS a
WHERE NOT EXISTS ( 
    SELECT  * 
    FROM GapsIslands AS b
    WHERE b.ID = a.ID AND b.SeqNo = a.SeqNo - 1)

Nun ist die Abfrage innerhalb der NOT EXISTS-Bedingung,

SELECT  * 
    FROM GapsIslands AS b
    WHERE b.ID = a.ID AND b.SeqNo = a.SeqNo - 1

gibt die Spalte zurück (nennen wir diese Spalte A) SeqNo as:

 SeqNo      
  1     
  5     
  8     
  9     
  20        
  25        

Die 'vollständige' Spalte (nennen wir diese Spalte B) für SeqNo lautet:

  SeqNo     
  1     
  2     
  5     
  6     
  8     
  9
  10        
  12        
  20        
  21        
  25
  26

Warum finden wir dann die Spalte, wenn wir Werte in Spalte B nehmen, in denen in Spalte A keine Werte vorhanden sind?

 SeqNo      
  1     
  5     
  8     
  12        
  20        
  25

Der Wert 1 in Spalte B ist auch IN Spalte A, warum erscheint dann der Wert 1 in der Spalte SeqNo der gesamten Abfrage?

4
TMilliman

Verschieben wir den problematischen Ausdruck von der Bedingung WHERE in die Ausgabeliste SELECT. EXISTS gibt einen Booleschen Wert an, und SQL Server möchte ihn nicht direkt anzeigen. Daher verwenden wir CASE und konvertieren ihn in eine lesbare Form.

SELECT ID, SeqNo, ROW_NUMBER() OVER (ORDER BY SeqNo) AS RowNum
/*   There we insert our EXISTS moved from WHERE   */
/*              and wrapped into CASE              */
, CASE WHEN EXISTS ( SELECT  * 
                     FROM GapsIslands AS b
                     WHERE b.ID = a.ID 
                       AND b.SeqNo = a.SeqNo - 1 )
       THEN 'Exists'      /* EXISTS is true there, NOT EXISIS is false */
       ELSE 'Not exists'  /* NOT EXISTS is true there, EXISIS is false */
       END AS previous_SeqNo_exists
/* ----------------------------------------------- */
FROM GapsIslands AS a
ORDER BY SeqNo

Geige

Die Abfrage gibt uns die nächste Ausgabe:

 ID | SeqNo | RowNum | previous_SeqNo_exists 
 -: | ----: | : ----- | : -------------------- 
 1 | 1 | 1 | Nicht vorhanden 
 1 | 2 | 2 | Existiert 
 1 | 5 | 3 | Nicht vorhanden 
 1 | 6 | 4 | Existiert 
 1 | 8 | 5 | Nicht vorhanden 
 1 | 9 | 6 | Existiert 
 1 | 10 | 7 | Existiert 
 1 | 12 | 8 | Nicht vorhanden 
 1 | 20 | 9 | Nicht vorhanden 
 1 | 21 | 10 | Existiert 
 1 | 25 | 11 | Nicht vorhanden 
 1 | 26 | 12 | Existiert 

Es scheint absolut klar zu sein. SeqNo = 1 hat keinen vorherigen Wert, weil es überhaupt keinen solchen Datensatz gibt, SeqNo = 5 (und alle anderen) - weil es vorher eine Lücke gibt.

Wenn wir diesen EXISTS Ausdruck in WHERE verwenden, werden die Datensätze mit "Exists" entfernt und wir erhalten nur Datensätze mit "Not existes" (vergessen Sie nicht, dass es zusätzliche NOT Operator, daher werden nur Datensätze zurückgegeben, die FALSE angeben.

ROW_NUMBER(), das nach WHERE funktioniert, listet einfach die zurückgegebenen Datensätze in der angegebenen Reihenfolge auf.

PS. Die betreffende Abfrage (veröffentlicht von OP) enthält keine ORDER BY-Klausel. Das ist schlecht. Dies kann dazu führen, dass die Datensätze in zufälliger Reihenfolge zurückgegeben werden. Trotzdem sind die von ROW_NUMBER() berechneten Datensatznummern korrekt, da die Funktion eine eigene, lokale Reihenfolge hat.

5
Akina