Ankündigung

Einklappen
Keine Ankündigung bisher.

Frage zu Unterabfragen (Anfänger)

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Frage zu Unterabfragen (Anfänger)

    Guten Tag,

    bin grad dabei SQL zu lernen. Jetzt komm ich zum folgenden Problem:

    sagen wir ich habe diese ein Abfrage (korreliert):

    1)
    SELECT T.KEY,T.*
    FROM T
    WHERE EXISTS (SELECT * FROM U WHERE U.KEY=T.KEY)

    und noch eine andere (unkorreliert):

    2)
    SELECT T.KEY,T.*
    FROM T
    WHERE T.Key IN (SELECT U.Key FROM U)

    Jetzt die Frage. Ich verstehe das nicht ganz. Beim 1. z.B., holt er immer einzelne Datensätze heraus, und vergleicht die dann, oder wie läuft das ganze ab? ... oder hat jemand ne gute Animation wo es eklärt wird?

  • #2
    Bei EXISTS vergleicht er für jede Zeile in T den Subquery. Hat der Subquery mindestens eine Zeile, dann ist EXISTS true, sonst false. Der Subquery wird also so oft ausgefährt wie du Zeilen in T hast.

    Bei IN wird der Subquery ausgeführt und das gleiche Resultset für alle Zeilen in T genommen. Der Subquery wird also genau einmal ausgeführt.

    Ein EXISTS ist sehr ineffizient und kann oft mittels eines Joins gelöst werden (kann aber doppelte Rows geben).
    [URL="https://github.com/chrisandchris"]GitHub.com - ChrisAndChris[/URL] - [URL="https://github.com/chrisandchris/symfony-rowmapper"]RowMapper und QueryBuilder für MySQL-Datenbanken[/URL]

    Kommentar


    • #3
      Also bei EXISTS z.B.:
      Zahlen sind Primärschlüssel z.B.

      PHP-Code:
      T2             U1
                       U
      2
                       U

      Also ergibt es true, da in T und U 2 existiert.

      PHP-Code:
      T3             U1
                       U
      2
                       U

      Es ergibt false, da in U keine 3 existiert, mit der verglichen wird.
      Und es geht für jeden Zeile in T so weiter... er prüft dann mit jedem U. Richtig so?

      Mit IN verstehe ich nicht, ich schreibe mal auf, wie ich es grad verstehe:
      PHP-Code:
      T2             U1
                       U
      2
                       U

      Der Prüft jetzt also so: 2 IN (1, 2, 4) -> true

      Jetzt hat er diese Werte gespeichert und muss sie nicht nochmal abfragen.
      Der ruft aber jetzt die neue Zeile auf.

      PHP-Code:
      T3             U1
                       U
      2
                       U

      Der Prüft so: 3 IN (1, 2, 4) -> false

      Richtig?

      Kommentar


      • #4
        Hinweis:

        Die Verwendung von "*" in SQL-Abfragen wird im Allgemeinen als schlechter Stil angesehen. "*" kann entweder alle Felder aus einer Tabelle, oder (wenn mehrere Tabellen mit einbezogen werden) aus allen Tabellen ziehen.

        Zwei Gründe sprechen dagegen: Wenn du dein Tabellenschema anpasst, wird die Abfrage weiterhin funktionieren. Die Abfrage wird in deinem Code aber Variablen (bzw. Array-Keys) erzeugen, die von deinem Programm so nicht erwartet werden. Das kann (je nachdem, wie deine Applikation aufgebaut ist) zu schwer nachvollziehbaren Problemen führen. Würdest du die Spaltennamen direkt in deiner SQL-Abfrage angeben, würde die SQL-Abfrage direkt lautstark fehlschlagen. Dann kannst du möglicherweise einen Alias für umbenannte Tabellenspalten angeben, ohne dass du deinen Applikationscode anpassen musst.

        Außerdem kann man durch eine explizite Angabe die Reihenfolge und die Anzahl der Tabellenspalten begrenzen. Die Reihenfolge kann je nach Applikation eine Rollen spielen und je weniger Felder man einbezieht, desto schneller wird das Ergebnis zum abfragenden Client übertragen.

        Kommentar


        • #5
          Code:
          CREATE TABLE `tableA` (
            `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
            `columnA` int(11) DEFAULT NULL,
            PRIMARY KEY (`id`)
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
          CREATE TABLE `tableB` (
            `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
            `columnB` int(11) DEFAULT NULL,
            PRIMARY KEY (`id`)
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
          Code:
          INSERT INTO `tableA` (`id`, `columnA`)
          VALUES
          	(1,1),
          	(2,2),
          	(3,3);
          INSERT INTO `tableB` (`id`, `columnB`)
          VALUES
          	(3,3);
          Code:
          select * from tableA where exists(select * from tableB where tableB.id = tableA.id)
          -- ergibt eine Zeile (key 3)
          Code:
          select * from tableA where tableA.id in (select tableB.id from tableB)
          -- ergibt eine Zeile (key 3)
          Das Resultat ist dasselbe. Nur wird bei EXISTS der Subquery für jede Zeile erneut ausgewertet während er bei IN für das ganze Statement genau einmal ausgewertet wird.
          [URL="https://github.com/chrisandchris"]GitHub.com - ChrisAndChris[/URL] - [URL="https://github.com/chrisandchris/symfony-rowmapper"]RowMapper und QueryBuilder für MySQL-Datenbanken[/URL]

          Kommentar


          • #6
            Wie die Subqueries ausgeführt werden ist von Datenbank zu Datenbank unterschiedlich. Selbst zwischen verschiedenen Versionen gibt es teils massive Unterschiede.
            Vor Mysql 5.6 wurden Subqueries im WHERE für jeden Datensatz ausgeführt der ohne diese Bedingung getroffen hat. Auch Query Nr 2, der wurde aber umgeschreiben und drei mal dürft ihr raten in was!? In Query Nr 1. Für ein EXISTS reicht ein Treffer, für IN braucht man alle Datensätze. Das war dann aber auch schon die einzige Optimierung für Subqueries die Mysql bis dahin bot. Daher war der Einsatz von Subqueries im WHERE nicht empfehlenswert.
            Erst mit Mysql 5.6 sind eine ganze menge Optimierungen dazu gekommen, die einen sinnvollen Einsatz von Subqueries erlauben.

            Siehe: http://dev.mysql.com/doc/refman/5.6/...imization.html

            Ein wenig übersichtlicher:
            https://mariadb.com/kb/en/mariadb/ma...mizations-map/

            Kommentar

            Lädt...
            X