Michael: VOR und NACH Folger eine SELECT Abfrage ermitteln

Mich beschäftigt eine Frage wie ich den Vorgänger und den Nachfolge bei einer SELECT Ausgabe ermittle.

SELECT id, name FROM table WHERE id =10

Wenn ich nun die Vorgänger ID und die Nachfolger ID ermitteln möchte, muss ich das mit zwei weiteren separaten SELECT Abfragen bewerkstelligen, oder kann ich das mit der ersten Abfrage auch?

  1. Hallo Michael,

    SELECT id, name FROM table WHERE id =10

    Bist du dir bewusst, dass 9 und 11 nicht automatisch Vorgänger bzw. Nachfolger sind?

    Bis demnächst
    Matthias

    --
    Du kannst das Projekt SELFHTML unterstützen,
    indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
    1. Hallo,

      SELECT id, name FROM table WHERE id =10

      Bist du dir bewusst, dass 9 und 11 nicht automatisch Vorgänger bzw. Nachfolger sind?

      dann wäre die Frage ja trivial und man bräuchte nicht SQL zu bemühen.
      Mich irritiert aber, dass Michael die ID, mit der er einen Datensatz auswählt, auch wieder in der Ergebnismenge haben möchte. Das erscheint mir irgendwie ... hmm, sinnfrei.

      Live long and pros healthy,
       Martin

      --
      Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
    2. Hallo Matthias,

      ich glaube, dann würde er nicht fragen 😀

      Rolf

      --
      sumpsi - posui - obstruxi
    3. Ja, das bin ich mir bewusst, da ich ja sonst einfach 9 und 11 ansteuern könnte. Deshlab möchte ich sie ja auch ermitteln

  2. Hallo Michael,

    man könnte das mit einem BETWEEN Konstrukt lösen. Ungetestet aus der Hüfte geschossen:

    SELECT id, name 
       FROM table 
       WHERE id BETWEEN IFNULL((SELECT MAX(id) from TABLE where ID<10), 10)
                    AND IFNULL((SELECT MIN(id) from TABLE where ID>10), 10)
    ORDER BY id
    

    Problem ist nur, wenn es den Vorgänger oder den Nachfolger nicht gibt - dann liefert der SELECT MAX bzw. SELECT MIN einen NULL Wert. Die IFNULL-Funktion ersetzt den durch die ID.

    Heißt also: Du bekommst von dieser Query 0-3 Werte, je nachdem, ob es die ID, ihren Vorgänger oder ihren Nachfolger gibt, und musst dann im Programm prüfen, ob die ID des Satzes kleiner als, gleich wie oder größer als die gesuchte ID ist, damit Du weißt, welchen Satz Du im Zugriff hast.

    Edit: Ich habe noch einen ORDER BY hinzugefügt, damit Du die Sätze auf jeden Fall aufsteigend bekommst.

    Unterm Strich sind das auch 3 Abfragen. Sie finden nur alle in einer einzigen DB Abfrage statt und der SQL Server denkt sich dann vielleicht eine nette Optimierung dazu aus.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Ingrid,

      Test nachgeholt - funktioniert so. IFNULL ist eine MYSQL-Spezialität, die offizielle SQL Funktion heißt COALESCE.

      Ich habe dann auch noch einen Explain gemacht. Meine Test-Table hatte 30 Rows, und das Ergebnis war, dass er die Subselects macht und dann einen full index scan. Gefiel mir nicht, und ich habe diese Variante probiert:

      SELECT id, preis FROM test.entries WHERE id = (SELECT MAX(id) FROM test.entries WHERE id < @foo)
      union all
      SELECT id, preis FROM test.entries WHERE id = @foo
      union all
      SELECT id, preis FROM test.entries WHERE id = (SELECT MIN(id) FROM test.entries WHERE id > @foo)
      

      Ergebnis waren zwei full table Scans für vorgänger und Nachfolger, und ein Single Row Select für den ID-Satz. Gefällt mir auch nicht, aber es kann an der geringen Datenmenge liegen.

      Du musst die Explains für deine DB selbst nochmal laufen lassen, falls die signifikant größer ist.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Das hier scheint noch schlimmer zu sein. Bei den MIN/MAX Queries meinte er immerhin, er hätte in den Subselects die Tables wegoptimiert (wie auch immer). Hierbei nicht mehr, und die Full Table Scans um die Vorgänger- und Nachfolger-ID dann herauszusuchen sind immer noch da.

        SELECT id, preis FROM test.entries WHERE id = (SELECT id FROM test.entries WHERE id < @foo order by id desc limit 1)
        union all
        SELECT id, preis FROM test.entries WHERE id = @foo
        union all
        SELECT id, preis FROM test.entries WHERE id = (SELECT id FROM test.entries WHERE id > @foo order by id asc limit 1)
        

        Ich muss das wohl mal mit einer größeren Table probieren, die muss ich mir aber erstmal generieren.

        Rolf

        --
        sumpsi - posui - obstruxi
    2. Hallo Rolf B,

      SELECT id, name 
         FROM table 
         WHERE id BETWEEN IFNULL((SELECT MAX(id) from TABLE where ID<10), 10)
                      AND IFNULL((SELECT MIN(id) from TABLE where ID>10), 10)
      ORDER BY id
      

      Ich habe mal gelernt, dass man einer ID nicht eine Bedeutung zumessen sollte, die sie nicht haben soll.

      Bis demnächst
      Matthias

      --
      Du kannst das Projekt SELFHTML unterstützen,
      indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
      1. Hallo Matthias,

        da es seitens des OP keinerlei Hinweise gab, was unter einer "vorhergehenden" und "nachfolgenden" Id zu verstehen ist, habe ich die numerische Reihenfolge verwendet. Stimmt natürlich, das ist eine gewagte Annahme.

        Wenn die vorher/nachher Ordnung durch etwas anderes bestimmt wird, z.B. ein "Record Created" Timestamp oder eine alphabetische Ordnung auf dem Namen, dann müsste der OP das kundtun.

        Rolf

        --
        sumpsi - posui - obstruxi
  3. Wenn ich nun die Vorgänger ID und die Nachfolger ID ermitteln möchte, muss ich das mit zwei weiteren separaten SELECT Abfragen bewerkstelligen, oder kann ich das mit der ersten Abfrage auch?

    Wenn deine MySQL-Version es untersützt, dann wären Window-Functions hierfür das Mittel der Wahl, insbesondere lag und lead.

    1. Hallo 1unitedpower,

      hast Du das ausprobiert? Ich habe kein MySQL 8 da, aber MariaDB 10 und MS SQL Server 2012 können auch LAG und LEAD und da liefert LEAD nur was wenn der Satz auch im Query-Result enthalten ist.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. hast Du das ausprobiert? Ich habe kein MySQL 8 da, aber MariaDB 10 und MS SQL Server 2012 können auch LAG und LEAD und da liefert LEAD nur was wenn der Satz auch im Query-Result enthalten ist.

        Ich habs nicht getestet, das könnte ein Problem sein, vielleicht kann man es mit einer Subquery lösen. Irgendwie so (?):

        
        select * from (
            select
                id,
                lead(id) over w as lead_id,
                lag(id) over w as lag_id
            from `table`
            window w as (order by id)
        ) where id = ?
        
        1. Hallo 1unitedpower,

          hab jetzt keine Gelegenheit zum ausprobieren - das müsste funktionieren. Aber es durchläuft die komplette Tabelle, nur um dann einen Satz auszufiltern. Keine Ahnung ob die DB das optimieren kann.

          Rolf

          --
          sumpsi - posui - obstruxi
  4. Hello,

    Mich beschäftigt eine Frage wie ich den Vorgänger und den Nachfolge bei einer SELECT Ausgabe ermittle.

    SELECT id, name FROM table WHERE id =10

    Wenn ich nun die Vorgänger ID und die Nachfolger ID ermitteln möchte, muss ich das mit zwei weiteren separaten SELECT Abfragen bewerkstelligen, oder kann ich das mit der ersten Abfrage auch?

    Ich habe Dich jetzt so verstanden, dass Du innerhalb einer Sortierung (z. B. Nachname), die IDs des Vorgängers und des Nachfolgers eines Datensatzes, dessen ID Du kennst, ermitteln willst?

    Genau dies haben wir hier schon einmal ausführlich durchgekaut mit Lösung. Wenn ich Dich also richtig verstanden habe, müsste man das Archiv danach nochmal intensiver durchforsten. So auf Anhieb kann ich leider nichts wiederfinden.

    Glück Auf
    Tom vom Berg

    --
    Es gibt nichts Gutes, außer man tut es!
    Das Leben selbst ist der Sinn.