Vinzenz Mai: MySQL Bereiche von bis abfragen

Beitrag lesen

Hallo,

SELECT ID FROM tab WHERE

ID IN (SELECT ID FROM tab WHERE Modus='h')
  AND (
    ID-1 NOT IN (SELECT ID FROM tab WHERE Modus='h')
    OR
    ID+1 NOT IN (SELECT ID FROM tab WHERE Modus='h')
  )

  

> So - und nun hab ich keine Idee mehr, wie ich weiter machen soll/muss, um dahin zu kommen wo ich hin will. Oder geht das vielleicht gar nicht in einer Abfrage?  
  
das geht schon - aber schön oder gar effizient ist [anders](https://forum.selfhtml.org/?t=209167&m=1423210) ...  
  
1\. Um zwischen Beginn und Ende eines Modus zu unterscheiden, selektieren wir immer nur den entsprechenden Abschnitt:  
  
~~~sql
SELECT  
    ID,  
    'h' modus,  
    'beginn' typ  
FROM  
    tab  
WHERE  
    ID IN (SELECT ID FROM tab WHERE Modus='h')  
AND  
    ID-1 NOT IN (SELECT ID FROM tab WHERE Modus='h')  
  
UNION ALL  
  
SELECT  
    ID,  
    'h',  
    'ende'  
FROM  
    tab  
WHERE  
    ID IN (SELECT ID FROM tab WHERE Modus='h')  
AND  
    ID+1 NOT IN (SELECT ID FROM tab WHERE Modus='h')  

2. Du siehst, dass Du für jeden Modus-Wert eine solche UNION dranhängen musst:

SELECT  
    ID,  
    'h' modus,  
    'beginn' typ  
FROM  
    tab  
WHERE  
    ID IN (SELECT ID FROM tab WHERE Modus='h')  
AND  
    ID-1 NOT IN (SELECT ID FROM tab WHERE Modus='h')  
  
UNION ALL  
  
SELECT  
    ID,  
    'h',  
    'ende'  
FROM  
    tab  
WHERE  
    ID IN (SELECT ID FROM tab WHERE Modus='h')  
AND  
    ID+1 NOT IN (SELECT ID FROM tab WHERE Modus='h')  
  
UNION ALL  
  
SELECT  
    ID,  
    'k',  
    'beginn'  
FROM  
    tab  
WHERE  
    ID IN (SELECT ID FROM tab WHERE Modus='k')  
AND  
    ID-1 NOT IN (SELECT ID FROM tab WHERE Modus='k')  
  
UNION ALL  
  
SELECT  
    ID,  
    'k',  
    'ende'  
FROM  
    tab  
WHERE  
    ID IN (SELECT ID FROM tab WHERE Modus='k')  
AND  
    ID+1 NOT IN (SELECT ID FROM tab WHERE Modus='k')  
  
ORDER BY  
    ID  

liefert Dir:

ID | modus | typ
-------------------
 2 |    k  | beginn
 4 |    k  | ende
 6 |    h  | beginn
 8 |    h  | ende
10 |    h  | beginn
12 |    h  | ende

Mit dieser Tabelle (als tab2) erhältst Du mit folgender korrelierten Unterabfrage

SELECT  
    i1.modus,  
    i1.ID vonID,  
    (SELECT MIN(i2.ID) FROM tab2 i2 WHERE i2.ID >  i1.ID) bisID  
FROM  
    tab2 i1  
WHERE  
    i1.typ = 'beginn'  
ORDER BY  
    i1.ID  

das von Dir gewünschte Ergebnis:

modus | vonID | bisID
---------------------
   k  |     2 |    4
   h  |     6 |    8
   h  |    10 |   12

Dabei nutzen wir aus, dass sich Beginn und Ende stets abwechseln. Wir suchen daher die kleinste ID, die größer ist als unser aktueller Beginn.

Nun kannst Du noch das wüste UNION-Konstrukt anstelle von tab2 (gleich zweimal!) einbauen und Du hast *eine* Abfrage, die Dir das gewünschte Ergebnis liefert:

  
SELECT  
  grenzen.modus,  
  grenzen.ID vonID,  
  (  
    SELECT  
      MIN(g2.ID)  
    FROM (  
      SELECT  
        i1.ID,  
        'h' modus,  
        'beginn' typ  
      FROM  
        tab i1  
      WHERE  
        ID IN (SELECT ID FROM tab WHERE Modus='h')  
      AND  
        ID-1 NOT IN (SELECT ID FROM tab WHERE Modus='h')  
  
      UNION ALL  
  
      SELECT  
        ID,  
        'h',  
        'ende'  
      FROM  
        tab  
      WHERE  
        ID IN (SELECT ID FROM tab WHERE Modus='h')  
      AND  
        ID+1 NOT IN (SELECT ID FROM tab WHERE Modus='h')  
  
      UNION ALL  
  
      SELECT  
        ID,  
        'k' modus,  
        'beginn' typ  
      FROM  
        tab  
      WHERE  
        ID IN (SELECT ID FROM tab WHERE Modus='k')  
      AND  
        ID-1 NOT IN (SELECT ID FROM tab WHERE Modus='k')  
  
      UNION ALL  
  
      SELECT  
        ID,  
        'k',  
        'ende'  
      FROM  
        tab  
      WHERE  
        ID IN (SELECT ID FROM tab WHERE Modus='k')  
      AND  
        ID+1 NOT IN (SELECT ID FROM tab WHERE Modus='k')  
    ) AS g2  
    WHERE  
      g2.ID >  grenzen.ID) bisID  
FROM (  
    SELECT  
      i1.ID,  
      'h' modus,  
      'beginn' typ  
    FROM  
      tab i1  
    WHERE  
      ID IN (SELECT ID FROM tab WHERE Modus='h')  
    AND  
      ID-1 NOT IN (SELECT ID FROM tab WHERE Modus='h')  
  
    UNION ALL  
  
    SELECT  
      ID,  
      'h',  
      'ende'  
    FROM  
      tab  
    WHERE  
      ID IN (SELECT ID FROM tab WHERE Modus='h')  
    AND  
      ID+1 NOT IN (SELECT ID FROM tab WHERE Modus='h')  
  
    UNION ALL  
  
    SELECT  
      ID,  
      'k' modus,  
      'beginn' typ  
    FROM  
      tab  
    WHERE  
      ID IN (SELECT ID FROM tab WHERE Modus='k')  
    AND  
      ID-1 NOT IN (SELECT ID FROM tab WHERE Modus='k')  
  
    UNION ALL  
  
    SELECT  
      ID,  
      'k',  
      'ende'  
    FROM  
      tab  
    WHERE  
      ID IN (SELECT ID FROM tab WHERE Modus='k')  
    AND  
      ID+1 NOT IN (SELECT ID FROM tab WHERE Modus='k')  
) AS grenzen  
WHERE  
  grenzen.typ = 'beginn'  
ORDER BY  
  grenzen.ID

Wenn Du die unterschiedlichen Modi zusätzlich ermitteln musst, wird's noch lustiger. Spätestens dann möchtest Du eine Stored Procedure schreiben, die ein einziges Mal durch die Ausgangstabelle (sortiert) iteriert und das Ergebnis zusammenstellt ...

Freundliche Grüße

Vinzenz