Christian Kruse: diffiziles SELECT

Hallo alle,

Tja, um es mal kurz zu machen: ich habe eine Datenstruktur mit zwei Tabellen, eine Tabelle
'documents' und eine Tabelle 'attributes'. Die Dokumenten-Tabelle hat zwei Felder: 'id' und
'type'. Die 'attributes'-Tabelle hatt die Felder 'id' (vererbt von documents(id)), 'name' und
'data'. Zwischen 'documents' und 'attributes' ist eine 1:n-Beziehung: jedem Dokument koennen n
Attribute zugeordnet werden. Ich weiss, dass das nicht die beste Datenstruktur ist, aber fuer
die Daten, die ich bekomme (naemlich wirklich sehr generische Daten) die einzige Moeglichkeit.

Nun habe ich das Problem, dass ich alle 'attributes'-Eintraege zu einem 'documents'-Eintrag
haben moechte, bei dem ein 'attributes'-Eintrag einen bestimmten Wert hat. Konkreter: es sind
unter anderem User-Daten in dieser Struktur gespeichert. Ich moechte jetzt einen bestimmten
User heraussuchen, mit allen Attributen.

Die einzige Moeglichkeit, die mir hier einfaellt, ist ein 3er-Join, in dem ich die
'attributes'-Tabelle 2x hineinziehe und dann per

'where join1.name = "username" AND join1.data = "name"'

einschraenke. Das ist mir allerdings etwas zu bloed, die Datenmengen koennen dadurch ziemlich
schnell zu gross werden. Habt ihr vielleicht eine Idee, wie ich das besser loesen kann?

Gruesse,
 CK

  1. Hoi,

    was ich vergessen habe zu sagen: ich muss leider MySQL verwenden (V. 3.23.52).

    Gruesse,
     CK

    1. Hai,

      was ich vergessen habe zu sagen: ich muss leider MySQL verwenden (V. 3.23.52).

      Gruesse,
       CK

      Alle Eintrage der documents-ID 1, wenn bei attributes-name "Maier" steht:

      SELECT documents.ID, documents.typ, attributes.name, attributes.data
      FROM documents INNER JOIN attributes ON documents.ID = attributes.ID
      WHERE documents.ID=1 AND attributes.name="Maier";

      oder habe ich das falsch verstanden?

      Gruß

      Axel

  2. Moin!

    Die einzige Moeglichkeit, die mir hier einfaellt, ist ein 3er-Join, in dem ich die
    'attributes'-Tabelle 2x hineinziehe und dann per

    'where join1.name = "username" AND join1.data = "name"'

    SELECT dasnötige... FROM document_table LEFT JOIN attrib_table on d_t.id=a_t.id WHERE a_t.name="username" AND a_t.data ="name"

    Sicherlich hab ich den Kern deiner Frage verfehlt. Wie sieht dein jetziges Select aus? Ich verstehe nicht, warum du 3x joinen mußt. Der entstehenden Tabelle hilft es sicherlich nicht, weil nur redundante Daten hinzukommen. Bedenke, dass in der ON-Anweisung nicht nur IDs verglichen werden müssen, sondern auch Feldinhalte abgefragt werden können.

    PS: Kann sein, dass ich MySQL-verwöhnt bin.

    --

     - Sven Rautenberg
    1. Hallo Sven,

      Die einzige Moeglichkeit, die mir hier einfaellt, ist ein 3er-Join, in dem ich die
      'attributes'-Tabelle 2x hineinziehe und dann per

      'where join1.name = "username" AND join1.data = "name"'

      SELECT dasnötige... FROM document_table LEFT JOIN attrib_table on d_t.id=a_t.id WHERE
      a_t.name="username" AND a_t.data ="name"

      Tja, so einfach ist das nicht. Damit haette ich das User-Dokument und die Attribute dazu, die
      als Namen 'username' und als daten 'name' haben. Nicht aber *alle* zu dem Dokument gehoerigen
      Attribute.

      Sicherlich hab ich den Kern deiner Frage verfehlt. Wie sieht dein jetziges Select aus? Ich
      verstehe nicht, warum du 3x joinen mußt.

      Damit ich nach einem Attribut filtern kann. Mit deinem Select wuerde ich genau ein Attribut
      herausfiltern, mit dem von mir vorgeschlagenen 3er-Join gaebe es ein karthesisches Produkt
      aus den 3 Tabellen, bei dem ich dann nach dem ersten Attribute-Join filtern koennte.

      Der entstehenden Tabelle hilft es sicherlich nicht, weil nur redundante Daten hinzukommen.

      Doch :)

      Gruesse,
       CK

      1. Moin!

        Tja, so einfach ist das nicht. Damit haette ich das User-Dokument und die Attribute dazu, die
        als Namen 'username' und als daten 'name' haben. Nicht aber *alle* zu dem Dokument gehoerigen
        Attribute.

        Kannst du mit einfachen Worten bzw. als Ergebnistabelle mal beschreiben, was du eigentlich willst? Dann wird's zumindest mir klar, dir vielleicht auch. :)

        Du hast:
        doc_tab:  id  type
        Beispieldaten?

        attr_tab: id  name data
        Beispieldaten?

        Du willst:
        erg_tab: id  type  name  data
        Beispieldaten?

        --

         - Sven Rautenberg
        1. Hallo Sven,

          Tja, so einfach ist das nicht. Damit haette ich das User-Dokument und die Attribute dazu,
          die als Namen 'username' und als daten 'name' haben. Nicht aber *alle* zu dem Dokument
          gehoerigen Attribute.

          Kannst du mit einfachen Worten bzw. als Ergebnistabelle mal beschreiben, was du eigentlich
          willst? Dann wird's zumindest mir klar, dir vielleicht auch. :)

          Du hast:
          doc_tab:  id  type
          Beispieldaten?

          1 5
          2 3
          3 5
          4 6

          attr_tab: id  name data
          Beispieldaten?

          1 "username" "ckruse"
          1 "password" "passwort"
          1 "vname"    "Christian"
          1 "nname    "Kruse"
          ....

          Du willst:
          erg_tab: id  type  name  data
          Beispieldaten?

          1 5 "username" "ckruse"
          1 5 "password" "passwort"
          1 5 "vname"    "Christian"
          1 5 "nname"    "Kruse"

          So. Wenn ich den von dir beschriebenen Join benutzen wuerde, wuerde ich eine Ergebnismenge
          von genau einer Reihe bekommen, naemlich:

          1 5 "username" "ckruse"

          Der von mir beschriebene 3er-Join wuerde (vor dem where) eine Tabelle wie folgt liefern:

          ID type  j1.name  j1.data  j2.name   j2.data
          1 5 "username" "ckruse"   "username" "ckruse"
          1 5 "username" "ckruse"   "password" "passwort"
          1 5 "username" "ckruse"   "vname"    "Christian"
          1 5 "username" "ckruse"   "nname"    "Kruse"
          1 5 "password" "passwort" "username" "ckruse"
          1 5 "password" "passwort" "passwort"
          1 5 "password" "passwort" "Christian"
          1 5 "password" "passwort" "Kruse"
          ....

          Eben ein karthesisches Produkt. Deshalb kann ich die Menge durch ein where beschraenken.
          Das ist mir aber zu bloed, die Datenmenge kann dadurch naemlich unglaublich gross werden.
          Deshalb: gibt es da etwas besseres, dass ich uebersehen habe? :)

          Gruesse,
           CK

          1. Moin!

            Du hast:
            doc_tab:  id  type
            Beispieldaten?

            1 5
            2 3
            3 5
            4 6

            attr_tab: id  name data
            Beispieldaten?

            1 "username" "ckruse"
            1 "password" "passwort"
            1 "vname"    "Christian"
            1 "nname    "Kruse"
            ....

            Du willst:
            erg_tab: id  type  name  data
            Beispieldaten?

            1 5 "username" "ckruse"
            1 5 "password" "passwort"
            1 5 "vname"    "Christian"
            1 5 "nname"    "Kruse"

            Das ist alles? Du willst zu einer eindeutig definierten ID in doc_tab alle Attribut-Einträge, die es dazu gibt? Ich sehe das Problem nicht so ganz. Die Abfrage erscheint mir ziemlich trivial. Vielleicht übersehe ich die ganze Zeit den Punkt, um den es dir wirklich geht.

            So. Wenn ich den von dir beschriebenen Join benutzen wuerde, wuerde ich eine Ergebnismenge
            von genau einer Reihe bekommen, naemlich:

            1 5 "username" "ckruse"

            Ähm, naja - hängt davon ab, was du so willst. Willst du eine Dokumenten-ID haben und zu denen alle Attribute wissen, oder hast du den Attributnamen und -wert und suchst dazu einerseits das zugehörige Dokument und andererseits rückwärts wieder alle anderen zugehörigen Attribute?

            In diesem Fall würde ich in der Tat ohne größeres Nachdenken erst mal zwei JOINs (von der attr_tab zur doc_tab zurück zur attr_tab) veranschlagen - wenn du den Dokumententyp entbehren kannst, würde sich das auf ein JOIN reduzieren.

            Der von mir beschriebene 3er-Join wuerde (vor dem where) eine Tabelle wie folgt liefern:

            ID type  j1.name  j1.data  j2.name   j2.data
            1 5 "username" "ckruse"   "username" "ckruse"
            1 5 "username" "ckruse"   "password" "passwort"
            1 5 "username" "ckruse"   "vname"    "Christian"
            1 5 "username" "ckruse"   "nname"    "Kruse"
            1 5 "password" "passwort" "username" "ckruse"
            1 5 "password" "passwort" "passwort"
            1 5 "password" "passwort" "Christian"
            1 5 "password" "passwort" "Kruse"
            ....

            Eben ein karthesisches Produkt. Deshalb kann ich die Menge durch ein where beschraenken.

            Kartesische Produkte saugen[tm]. :)

            Wenn du die Bildung der geJOINten Tabelle durch geeignete Formulierung des ON-Statements regulierst, kriegst du zumindest die Tabelle schon während des Enstehens kleiner, weil unerwünschte Kombinationen unterbleiben.

            Sorry, aber ich bin mir sicher, dass ich deine Aufgabe noch nicht ganz verstanden habe. Deshalb wohl auch der Threadtitel.

            SELECT a2.name, a2.data, d.type, d.id FROM a LEFT JOIN a AS a2 ON a.id=a2.id AND a.name="username" AND a.data ="ckruse" LEFT JOIN d ON a.id=d.id

            Wenn ich nicht vollkommen danebenliege, dann sollte das so ungefähr liefern, was dich interessiert. Kann ich leider nicht ausprobieren - mein MySQL streikt gerade irgendwie.

            --

             - Sven Rautenberg
            1. Hallo Sven,

              Das ist alles? Du willst zu einer eindeutig definierten ID in doc_tab alle
              Attribut-Einträge, die es dazu gibt?

              Nein. Ich will zu einem Usernamen (der als Attribut gespeichert ist) das Dokument sowie alle
              Attribute.

              Ich sehe das Problem nicht so ganz. Die Abfrage erscheint mir ziemlich trivial.
              Vielleicht übersehe ich die ganze Zeit den Punkt, um den es dir wirklich geht.

              Offensichtlich.

              In diesem Fall würde ich in der Tat ohne größeres Nachdenken erst mal zwei JOINs (von der
              attr_tab zur doc_tab zurück zur attr_tab) veranschlagen

              Das ist das, was ich sagte. Der Trick mit dem karthesischen Produkt.

              Sorry, aber ich bin mir sicher, dass ich deine Aufgabe noch nicht ganz verstanden habe.
              Deshalb wohl auch der Threadtitel.

              SELECT a2.name, a2.data, d.type, d.id FROM a LEFT JOIN a AS a2 ON a.id=a2.id AND
              a.name="username" AND a.data ="ckruse" LEFT JOIN d ON a.id=d.id

              Das ist dasselbe in gruen. Du hast nur die Tabellen anders angeordnet. Du bildest ein
              karthesisches Produkt und filterst das mit WHERE.

              Wenn ich nicht vollkommen danebenliege, dann sollte das so ungefähr liefern, was dich
              interessiert.

              Richtig -- aber meine Frage war, ob es nicht was sinnvolleres gibt :)

              Gruesse,
               CK

              1. Moin!

                Sorry, aber ich bin mir sicher, dass ich deine Aufgabe noch nicht ganz verstanden habe.
                Deshalb wohl auch der Threadtitel.

                SELECT a2.name, a2.data, d.type, d.id FROM a LEFT JOIN a AS a2 ON a.id=a2.id AND
                a.name="username" AND a.data ="ckruse" LEFT JOIN d ON a.id=d.id

                Das ist dasselbe in gruen. Du hast nur die Tabellen anders angeordnet. Du bildest ein
                karthesisches Produkt und filterst das mit WHERE.

                Nein, mache ich nicht. Das ist ja gerade der Trick mit dem ON-Statement: Es wird nicht das Kreuzprodukt (jede Zeile der einen Tabelle wird mit jeder anderen Zeile der anderen Tabelle verknüpft und dann rausgefiltert, was gewünscht ist) gebildet, sondern es werden nur die Verknüpfungen in die Ergebnistabelle gelegt, die der ON-Bedingung (geprüft vor der Verknüpfung) entsprechen.

                Siehe auch http://www.little-idiot.de/mysql/mysql-118.html:
                "Der einfachste JOIN ist der sogenannte "EQUI-JOIN". Ein Beispiel :

                SELECT A.EineSpalte, B.EineAndereSpalte
                FROM Tabelle1 AS A, Tabelle2 AS B WHERE A.EinWert = B.EinAndererWert;

                Man kann ihn aber auch ganz anders schreiben, und die Ergebnismenge wird die gleiche sein, nämlich so :

                SELECT A.EineSpalte, B.EineAndereSpalte
                FROM Tabelle1 AS A JOIN Tabelle2 AS B
                ON A.EinWert = B.EinAndererWert;

                Wenn die Ergebnismenge die gleiche ist, wo liegt dann der Unterschied zwischen diesen beiden Formen ? Gibt es überhaupt einen Unterschied ?

                Der Unterschied liegt in der Laufzeit. Im ersten Beispiel wird zuerst das kartesische Produkt aus beiden Tabellen gebildet (jede Zeile aus Tabelle1 wird mit jeder Zeile aus Tabelle2 verknüpft), und wenn beide Tabellen nur jeweils 100 Zeilen enthalten, sind das schon 10.000 Zeilen in der temporären Zwischentabelle. Erst dann werden die Zeilen gelöscht, die nicht die WHERE-Bedingung erfüllen."

                Scheint mir jedenfalls wesentlich besser zu sein, als mit WHERE zu arbeiten. Die MySQL-Doku (http://www.mysql.com/documentation/mysql/bychapter/manual_MySQL_Optimisation.html#LEFT_JOIN_optimisation) sagt zu den Optimierungen, die bei LEFT JOIN vorgenommen werden, allerdings, dass ON und WHERE gleich behandelt werden (alles in ON wird nach WHERE befördert und optimiert). Ich kann mir dann nicht vorstellen, dass MySQL sowas aufwendiges wie ein vollständiges kartesisches Produkt im Speicher anlegt, um mit WHERE Zeilen rauszufiltern, sondern die ganze Sache von vornherein optimiert.

                Wenn du auf MySQL angewiesen bist, bleibt dir also nichts anderes übrig, als zu nehmen, was möglich ist. Man kann sicherlich mit der Tabellenreihenfolge noch experimentieren (da gibts offenbar laut MySQL Unterschiede, man kann Full Table Scans vermeiden helfen), aber um das JOINen kommst du nicht drum herum.

                --

                 - Sven Rautenberg
                1. Hallo Sven,

                  Nein, mache ich nicht. Das ist ja gerade der Trick mit dem ON-Statement: Es wird nicht das Kreuzprodukt (jede Zeile der einen Tabelle wird mit jeder anderen Zeile der anderen Tabelle verknüpft und dann rausgefiltert, was gewünscht ist) gebildet, sondern es werden nur die Verknüpfungen in die Ergebnistabelle gelegt, die der ON-Bedingung (geprüft vor der Verknüpfung) entsprechen.

                  Also, Sven, jetzt muss ich dich ruegen. Natuerlich tust du das. Du holst dir die erste Spalte per JOIN in die Ergebnismenge. Das Ergebnis sieht ca. so aus:

                  mysql> SELECT documents.id,documents.type,attributes.id,attributes.name,attributes.data FROM documents LEFT JOIN attributes ON attributes.did = documents.id WHERE documents.id = 1; +----+------+------+----------+------------+ | id | type | id   | name     | data       | +----+------+------+----------+------------+ |  1 |    2 |    1 | Received | 61VlXQaSUQ | |  1 |    2 |    2 | Received | FDBRcP8mba | |  1 |    2 |    3 | Cc       | 9PRNBWUEsS | |  1 |    2 |    4 | Subject  | pfj6fC.tNy | |  1 |    2 |    5 | To       | Vlb4QscpUT | |  1 |    2 |    6 | Bcc      | qroXa5C1.n | |  1 |    2 |    7 | From     | wSLfs2mLG5 | +----+------+------+----------+------------+ 7 rows in set (0.01 sec)

                  mysql>

                  Jetzt holst du dir die 'attributes'-Spalte ein weiteres mal hinzu, auch ueber einen JOIN:

                  mysql> SELECT documents.id,documents.type,attributes.id,attributes.name,attributes.data,a2.id,a2.name,a2.data FROM documents LEFT JOIN attributes ON attributes.did = documents.id LEFT JOIN attributes AS a2 ON a2.did = documents.id WHERE documents.id = 1; +----+------+------+----------+------------+------+----------+------------+ | id | type | id   | name     | data       | id   | name     | data       | +----+------+------+----------+------------+------+----------+------------+ |  1 |    2 |    1 | Received | 61VlXQaSUQ |    1 | Received | 61VlXQaSUQ | |  1 |    2 |    1 | Received | 61VlXQaSUQ |    2 | Received | FDBRcP8mba | |  1 |    2 |    1 | Received | 61VlXQaSUQ |    3 | Cc       | 9PRNBWUEsS | |  1 |    2 |    1 | Received | 61VlXQaSUQ |    4 | Subject  | pfj6fC.tNy | |  1 |    2 |    1 | Received | 61VlXQaSUQ |    5 | To       | Vlb4QscpUT | |  1 |    2 |    1 | Received | 61VlXQaSUQ |    6 | Bcc      | qroXa5C1.n | |  1 |    2 |    1 | Received | 61VlXQaSUQ |    7 | From     | wSLfs2mLG5 | |  1 |    2 |    2 | Received | FDBRcP8mba |    1 | Received | 61VlXQaSUQ | |  1 |    2 |    2 | Received | FDBRcP8mba |    2 | Received | FDBRcP8mba | |  1 |    2 |    2 | Received | FDBRcP8mba |    3 | Cc       | 9PRNBWUEsS | |  1 |    2 |    2 | Received | FDBRcP8mba |    4 | Subject  | pfj6fC.tNy | |  1 |    2 |    2 | Received | FDBRcP8mba |    5 | To       | Vlb4QscpUT | |  1 |    2 |    2 | Received | FDBRcP8mba |    6 | Bcc      | qroXa5C1.n | |  1 |    2 |    2 | Received | FDBRcP8mba |    7 | From     | wSLfs2mLG5 | |  1 |    2 |    3 | Cc       | 9PRNBWUEsS |    1 | Received | 61VlXQaSUQ | |  1 |    2 |    3 | Cc       | 9PRNBWUEsS |    2 | Received | FDBRcP8mba | |  1 |    2 |    3 | Cc       | 9PRNBWUEsS |    3 | Cc       | 9PRNBWUEsS | |  1 |    2 |    3 | Cc       | 9PRNBWUEsS |    4 | Subject  | pfj6fC.tNy | |  1 |    2 |    3 | Cc       | 9PRNBWUEsS |    5 | To       | Vlb4QscpUT | |  1 |    2 |    3 | Cc       | 9PRNBWUEsS |    6 | Bcc      | qroXa5C1.n | |  1 |    2 |    3 | Cc       | 9PRNBWUEsS |    7 | From     | wSLfs2mLG5 | |  1 |    2 |    4 | Subject  | pfj6fC.tNy |    1 | Received | 61VlXQaSUQ | |  1 |    2 |    4 | Subject  | pfj6fC.tNy |    2 | Received | FDBRcP8mba | |  1 |    2 |    4 | Subject  | pfj6fC.tNy |    3 | Cc       | 9PRNBWUEsS | |  1 |    2 |    4 | Subject  | pfj6fC.tNy |    4 | Subject  | pfj6fC.tNy | |  1 |    2 |    4 | Subject  | pfj6fC.tNy |    5 | To       | Vlb4QscpUT | |  1 |    2 |    4 | Subject  | pfj6fC.tNy |    6 | Bcc      | qroXa5C1.n | |  1 |    2 |    4 | Subject  | pfj6fC.tNy |    7 | From     | wSLfs2mLG5 | |  1 |    2 |    5 | To       | Vlb4QscpUT |    1 | Received | 61VlXQaSUQ | |  1 |    2 |    5 | To       | Vlb4QscpUT |    2 | Received | FDBRcP8mba | |  1 |    2 |    5 | To       | Vlb4QscpUT |    3 | Cc       | 9PRNBWUEsS | |  1 |    2 |    5 | To       | Vlb4QscpUT |    4 | Subject  | pfj6fC.tNy | |  1 |    2 |    5 | To       | Vlb4QscpUT |    5 | To       | Vlb4QscpUT | |  1 |    2 |    5 | To       | Vlb4QscpUT |    6 | Bcc      | qroXa5C1.n | |  1 |    2 |    5 | To       | Vlb4QscpUT |    7 | From     | wSLfs2mLG5 | |  1 |    2 |    6 | Bcc      | qroXa5C1.n |    1 | Received | 61VlXQaSUQ | |  1 |    2 |    6 | Bcc      | qroXa5C1.n |    2 | Received | FDBRcP8mba | |  1 |    2 |    6 | Bcc      | qroXa5C1.n |    3 | Cc       | 9PRNBWUEsS | |  1 |    2 |    6 | Bcc      | qroXa5C1.n |    4 | Subject  | pfj6fC.tNy | |  1 |    2 |    6 | Bcc      | qroXa5C1.n |    5 | To       | Vlb4QscpUT | |  1 |    2 |    6 | Bcc      | qroXa5C1.n |    6 | Bcc      | qroXa5C1.n | |  1 |    2 |    6 | Bcc      | qroXa5C1.n |    7 | From     | wSLfs2mLG5 | |  1 |    2 |    7 | From     | wSLfs2mLG5 |    1 | Received | 61VlXQaSUQ | |  1 |    2 |    7 | From     | wSLfs2mLG5 |    2 | Received | FDBRcP8mba | |  1 |    2 |    7 | From     | wSLfs2mLG5 |    3 | Cc       | 9PRNBWUEsS | |  1 |    2 |    7 | From     | wSLfs2mLG5 |    4 | Subject  | pfj6fC.tNy | |  1 |    2 |    7 | From     | wSLfs2mLG5 |    5 | To       | Vlb4QscpUT | |  1 |    2 |    7 | From     | wSLfs2mLG5 |    6 | Bcc      | qroXa5C1.n | |  1 |    2 |    7 | From     | wSLfs2mLG5 |    7 | From     | wSLfs2mLG5 | +----+------+------+----------+------------+------+----------+------------+ 49 rows in set (0.01 sec)

                  mysql>

                  So, und was ist das anderes als ein karthesisches Produkt aus den beiden JOIN-Tabellen?

                  Ueberigens ist deine Abfrage eh falsch ;) Sie wuerde auch wieder nur eine Reihe bringen, wegen des JOIN ueber die Attributs-ID. Ich bin zu dem Schluss gekommen, dass folgendes die einzige Moeglichkeit ist:

                  SELECT   documents.id,   documents.type,   attributes.id AS aid,   attributes.name,   attributes.data  FROM   documents  LEFT JOIN    attributes   ON    attributes.did = documents.id  LEFT JOIN    attributes AS a2   ON    a2.did = documents.id  WHERE    a2.name = "username"   AND    a2.data = "ckruse";

                  Die Ergebnismenge ist (korrekterweise):

                  +-------+------+-------+----------+---------+ | id    | type | aid   | name     | data    | +-------+------+-------+----------+---------+ | 10001 |    5 | 45110 | username | ckruse  | | 10001 |    5 | 45111 | password | gjm270z | | 10001 |    5 | 45112 | nname    | Kruse   | +-------+------+-------+----------+---------+

                  Das ist allerdings etwas lahmarschig, wie gesagt. Deshalb werde ich das wohl in zwei Selects machen.

                  Gruesse,  CK

                2. Hi Sven

                  Nein, mache ich nicht. Das ist ja gerade der Trick mit dem ON-Statement: Es wird nicht das Kreuzprodukt (jede Zeile der einen Tabelle wird mit jeder anderen Zeile der anderen Tabelle verknüpft und dann rausgefiltert, was gewünscht ist) gebildet, sondern es werden nur die Verknüpfungen in die Ergebnistabelle gelegt, die der ON-Bedingung (geprüft vor der Verknüpfung) entsprechen.

                  Logisch bleibt es ein kartesisches Produkt, nur technisch ist es optimiert.
                  Für Performance hast du aber recht.

                  Wenn die Ergebnismenge die gleiche ist, wo liegt dann der Unterschied zwischen diesen beiden Formen ? Gibt es überhaupt einen Unterschied ?

                  Der Unterschied liegt in der Laufzeit. Im ersten Beispiel wird zuerst das kartesische Produkt aus beiden Tabellen gebildet (jede Zeile aus Tabelle1 wird mit jeder Zeile aus Tabelle2 verknüpft), und wenn beide Tabellen nur jeweils 100 Zeilen enthalten, sind das schon 10.000 Zeilen in der temporären Zwischentabelle. Erst dann werden die Zeilen gelöscht, die nicht die WHERE-Bedingung erfüllen."

                  Diese Information ist veraltet, das war früher bei Mysql mal so, inzwischen
                  (seit mehr als 1.5Jahren) wird auch Where genauso optimiert.

                  Scheint mir jedenfalls wesentlich besser zu sein, als mit WHERE zu arbeiten. Die MySQL-Doku (http://www.mysql.com/documentation/mysql/bychapter/manual_MySQL_Optimisation.html#LEFT_JOIN_optimisation) sagt zu den Optimierungen, die bei LEFT JOIN vorgenommen werden, allerdings, dass ON und WHERE gleich behandelt werden (alles in ON wird nach WHERE befördert und optimiert). Ich kann mir dann nicht vorstellen, dass MySQL sowas aufwendiges wie ein vollständiges kartesisches Produkt im Speicher anlegt, um mit WHERE Zeilen rauszufiltern, sondern die ganze Sache von vornherein optimiert.

                  Aber nur weil es schöner ist und näher am ISO-Standard liegt (der
                  wäre in dem Fall Natural Join tabellenname [USING (Spalte)] wobei das Using
                  nur der Übersicht dient und falls mehrere Spalten mit dem selben Namen
                  existieren.

                  Gruss Daniela

  3. Hi Christian

    Tja, um es mal kurz zu machen: ich habe eine Datenstruktur mit zwei Tabellen, eine Tabelle
    'documents' und eine Tabelle 'attributes'. Die Dokumenten-Tabelle hat zwei Felder: 'id' und
    'type'. Die 'attributes'-Tabelle hatt die Felder 'id' (vererbt von documents(id)), 'name' und
    'data'. Zwischen 'documents' und 'attributes' ist eine 1:n-Beziehung: jedem Dokument koennen n
    Attribute zugeordnet werden. Ich weiss, dass das nicht die beste Datenstruktur ist, aber fuer
    die Daten, die ich bekomme (naemlich wirklich sehr generische Daten) die einzige Moeglichkeit.

    Die Datenstruktur ist doch okay, eine typische Metadatenstruktur halt...

    Mein Vorschlag:
       SELECT id, name, date FROM attributes WHERE id = (SELECT id FROM attributes WHERE name = "username" AND data = "name"')

    oder:
       SELECT id, name, date FROM attributes WHERE id IN (SELECT id FROM attributes WHERE name = "username" AND data = "name"')

    Ich weiss gerade nicht, welche der beiden Varianten in MySQL funktioniert, da ich gerade keine solche DB im Zugriff habe.

    Grüsse
    Eisbär

    1. Hallo Eisbär,

      Die Datenstruktur ist doch okay, eine typische Metadatenstruktur halt...

      Naja, ich mag solche Datenstrukturen nicht. Sind unhandlich.

      Mein Vorschlag:
         SELECT id, name, date FROM attributes WHERE id = (SELECT id FROM attributes WHERE name = "username" AND data = "name"')

      oder:
         SELECT id, name, date FROM attributes WHERE id IN (SELECT id FROM attributes WHERE name = "username" AND data = "name"')

      Hrhr :) Deshalb meine Bemerkung, ich muesse leider MySQL benutzen. MySQL kann keine Subselects.

      Gruesse,
       CK

      1. Hi Christian

        Hrhr :) Deshalb meine Bemerkung, ich muesse leider MySQL benutzen. MySQL kann keine Subselects.

        Mein Beileid ;-)
        Ich bin richtig froh, dass ich noch nie zwingend auf MySQL angewiesen war. Subselects kriegt sogar Access hin (*hihi*).

        Wie ist ein DBA-Leben ohne Subselects möglich... ;-)

        Grüsse
        Eisbär

      2. Hallo,

        Mein Vorschlag:
           SELECT id, name, date FROM attributes WHERE id = (SELECT id FROM attributes WHERE name = "username" AND data = "name"')

        oder:
           SELECT id, name, date FROM attributes WHERE id IN (SELECT id FROM attributes WHERE name = "username" AND data = "name"')

        Muss es denn unbedingt alles in ein SQL-Statement? Hast Du kein Programm in dem Du die SQL-Statements abfragst? Dann frag doch erst ein Recordset:

        SELECT id FROM attributes WHERE name = "username" AND data = "name"

        ab, und lass in einer Schleife für alle $id in dem Recordset:

        SELECT id, name, date FROM attributes WHERE id = $id

        ermitteln.

        Gruß

        Axel

        1. Hallo Axel,

          Muss es denn unbedingt alles in ein SQL-Statement? Hast Du
          kein Programm in dem Du die SQL-Statements abfragst? Dann
          frag doch erst ein Recordset:

          Das ist es eben, was ich wahrscheinlich tun werden.

          Gruesse,
           CK

    2. Hi nochmals

      Ups, hier ist noch ein Fehler drin:

      Mein Vorschlag:
         SELECT id, name, date FROM attributes WHERE id = (SELECT id FROM attributes WHERE name = "username" AND data = "name"')

      ^ gehört weg!

      oder:
         SELECT id, name, date FROM attributes WHERE id IN (SELECT id FROM attributes WHERE name = "username" AND data = "name"')

      ^ gehört weg!

      Sorry ...

      Grüsse
      Eisbär

  4. Tja, um es mal kurz zu machen: ich habe eine Datenstruktur mit zwei Tabellen, eine Tabelle
    'documents' und eine Tabelle 'attributes'. Die Dokumenten-Tabelle hat zwei Felder: 'id' und
    'type'. Die 'attributes'-Tabelle hatt die Felder 'id' (vererbt von documents(id)), 'name' und
    'data'. Zwischen 'documents' und 'attributes' ist eine 1:n-Beziehung: jedem Dokument koennen n
    Attribute zugeordnet werden. Ich weiss, dass das nicht die beste Datenstruktur ist, aber fuer
    die Daten, die ich bekomme (naemlich wirklich sehr generische Daten) die einzige Moeglichkeit.

    Bis hierher hört sich das nach einer normalen 1:n-Beziehung an - warum nicht die beste Datenstruktur? Was sind "sehr generische" Daten?

    Nun habe ich das Problem, dass ich alle 'attributes'-Eintraege zu einem 'documents'-Eintrag
    haben moechte, bei dem ein 'attributes'-Eintrag einen bestimmten Wert hat. Konkreter: es sind
    unter anderem User-Daten in dieser Struktur gespeichert. Ich moechte jetzt einen bestimmten
    User heraussuchen, mit allen Attributen.

    Ist das so richtig verstanden:

    documents            attributes
    ---------            ----------
     id         ---->>    id
     type                 name
                          data

    Du willst zu einer doc.id _alle_ n zugehörigen att-Sätze, und zwar jene doc.id, die auf der n-Seite ein Feld mit einem bestimmten Inhalt aufweist? Hört sich nach Subselect an; da das in Deiner MySQL-Version ausscheidet, sehe ich als einzige Alternative zu Deinem Ansatz einen zweistufigen SELECT. Da MySQL auch für SELECTs in Tabellen zu impotent ist, könntest Du mit einem SELECT INTO OUTFILE aus attributes die Sätze heraussuchen, welche Deinem Kriterium entsprechen, dann mit LOAD DATA das Ergebnis in eine Tabelle laden und in einem zweiten SELECT das Endergebnis aufbauen.

    Sehr holprig - aber wenn auf der n-Seite nur wenige Sätze Deinem Kriterium entsprechen, könnte es bei sehr großen Tabellen performanter und ressourcenschonender sein als Dein (beinahe) doppeltes Kreuzprodukt.

    HTH Robert

    1. Moin,

      Da MySQL auch für SELECTs in Tabellen zu impotent ist, könntest Du mit einem SELECT INTO OUTFILE aus attributes die Sätze heraussuchen, welche Deinem Kriterium entsprechen, dann mit LOAD DATA das Ergebnis in eine Tabelle laden

      Du möchtest dir evt. mal http://www.mysql.com/documentation/mysql/bychapter/manual_Reference.html#INSERT_SELECT ansehen.

      --
      Henryk Plötz
      Grüße aus Berlin
      Quantum Mechanics is God's version of "Trust me."
    2. Hallo srob,

      Bis hierher hört sich das nach einer normalen 1:n-Beziehung an

      Richtig.

      • warum nicht die beste Datenstruktur?

      Weil sie umstaendlich und unhandlich ist.

      Was sind "sehr generische" Daten?

      Nun, Daten, deren Aufbau nicht 100% bekannt ist. In diese Datenstruktur koennte man EMails
      stecken, User-Daten, Adress-Daten, etc, etc.

      Nun habe ich das Problem, dass ich alle 'attributes'-Eintraege zu einem
      'documents'-Eintrag haben moechte, bei dem ein 'attributes'-Eintrag einen bestimmten
      Wert hat. Konkreter: es sind unter anderem User-Daten in dieser Struktur gespeichert.
      Ich moechte jetzt einen bestimmten User heraussuchen, mit allen Attributen.

      Ist das so richtig verstanden:

      documents            attributes
      ---------            ----------
      id         ---->>    id
      type                 name
                            data

      Richtig.

      Du willst zu einer doc.id _alle_ n zugehörigen att-Sätze, und zwar jene doc.id, die auf
      der n-Seite ein Feld mit einem bestimmten Inhalt aufweist?

      Richtig.

      Hört sich nach Subselect an;

      Bei PG haette ich das auch so geloest ;)

      da das in Deiner MySQL-Version ausscheidet, sehe ich als einzige Alternative zu Deinem
      Ansatz einen zweistufigen SELECT.

      *seufz* Ich hatte es befuerchtet.

      Da MySQL auch für SELECTs in Tabellen zu impotent ist,

      *Das* stimmt nicht. 'INSERT INTO statemet SELECT statement' ist durchaus moeglich.

      könntest Du mit einem SELECT INTO OUTFILE aus attributes die Sätze heraussuchen, welche
      Deinem Kriterium entsprechen, dann mit LOAD DATA das Ergebnis in eine Tabelle laden und
      in einem zweiten SELECT das Endergebnis aufbauen.

      Uarghs. Gehts noch ekeliger? Da waere ein INSERT INTO ... SELECT aber wesentlich sinnvoller.

      Sehr holprig - aber wenn auf der n-Seite nur wenige Sätze Deinem Kriterium entsprechen,
      könnte es bei sehr großen Tabellen performanter und ressourcenschonender sein als Dein
      (beinahe) doppeltes Kreuzprodukt.

      Hm. Mal sehen, ich werde es mal benchmarken. Besten Dank.

      Gruesse,
       CK

      1. Hi

        ...

        • warum nicht die beste Datenstruktur?

        Weil sie umstaendlich und unhandlich ist.

        Was sind "sehr generische" Daten?

        Nun, Daten, deren Aufbau nicht 100% bekannt ist. In diese Datenstruktur koennte man EMails
        stecken, User-Daten, Adress-Daten, etc, etc.

        Das eine hängt direkt mit dem anderen zusammen. Metadatenstrukturen sind extrem flexibel, sind aber in den SQL-Statements aufwändiger zu handhaben.

        Dafür kannst Du über Metadatenstrukturen wirklich fast alles abbilden, auch bei Datenbeständen die auf den ersten Blick sehr flexibel, bzw. uneinheitlich strukturiert sind.

        Die höhere Abstraktion und damit verbundenen Flexibiltät hat dafür seinen Preis...

        Grüsse
        Eisbär

        PS: Falls Du bei diesem Projekt extensiv solche Metadatenstrukturen verwendest, solltest Du über einen Wechsel der DB-Engine nachdenken, da Du in diesem Fall ohne Subselects unglücklich wirst.

        1. Hallo Eisbär,

          Das eine hängt direkt mit dem anderen zusammen. Metadatenstrukturen sind extrem flexibel,
          sind aber in den SQL-Statements aufwändiger zu handhaben.

          Das ist mir durchaus klar, ja :)

          Dafür kannst Du über Metadatenstrukturen wirklich fast alles abbilden, auch bei
          Datenbeständen die auf den ersten Blick sehr flexibel, bzw. uneinheitlich strukturiert sind.

          Das war der Grund, warum ich diese Datenstruktur gewaehlt habe.

          PS: Falls Du bei diesem Projekt extensiv solche Metadatenstrukturen verwendest, solltest Du
          über einen Wechsel der DB-Engine nachdenken, da Du in diesem Fall ohne Subselects
          unglücklich wirst.

          Das liegt nicht in meiner Hand. Ich persoenlich haette gern PostGreSQL :)

          Gruesse,
           CK

      2. Hallo Christian & Henryk,

        *Das* stimmt nicht. 'INSERT INTO statemet SELECT statement' ist durchaus moeglich.

        danke für die Erleuchtung! Meine Annahme mit dem Target der INTO-Klausel stammt aus Monty Widenius' Buch "MySQL": in der Befehlsreferenz Appendix D ist für SELECT als einziges Ziel OUTFILE angegeben. Da ich im normalen Leben immer mit SELECT INTO TABLE arbeite, habe ich nur das gesehen, was ich sehen wollte - selektive Wahrnehmung! Nachdem ich in der MySQL-Online-Doku nachgesehen habe, scheint es, daß neben INSERT INTO .. SELECT auch CREATE TABLE .. SELECT einen Ansatz bietet.

        Wenn Dir diese Möglichkeiten von MySQL vorher bekannt waren - war dann nicht auch klar, daß die geschachtelten JOINs in Relation zur zweistufigen Lösung indiskutabel sind? Dann sehe ich als einziges Motiv für Deinen Thread das (auch mir oft eigene) Gefühl, ein einzelnes SELECT-Statement sei irgendwie "geschlossener", "anspruchsvoller" oder etwas ähnlich Unbestimmtes; ab einer gewissen Komplexität der Strukturen und Größe der Datenmengen ist das aber nicht mehr der Fall, und mitnichten sind derart verwurstete Statements mit riesigen Zwischenmengen _elegant_ [1].

        Danke für die Horizonterweiterung!

        Robert

        [1] Nostalgische Erinnerung an ein Zitat meines alten Informatikprofessors: "Es ist die vornehmste Aufgabe eines Algorithmus', elegant zu sein!"