diffiziles SELECT
Christian Kruse
- datenbank
0 Christian Kruse0 Sven Rautenberg0 Eisbär0 srob0 Henryk Plötz0 Christian Kruse
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
Hoi,
was ich vergessen habe zu sagen: ich muss leider MySQL verwenden (V. 3.23.52).
Gruesse,
CK
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
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.
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
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?
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
Moin!
Du hast:
doc_tab: id type
Beispieldaten?1 5
2 3
3 5
4 6attr_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.
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
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.idDas 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.
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
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
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
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
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
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
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
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
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
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.
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
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.
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
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!"