UNION in IN() als Teil einer WHERE-Klausel? Zwischenstand.
bearbeitet von AugeHallo
> > > Muss es unbedingt eine einzelne Monster-Query sein, oder kannst du das Union-Ergebnis erstmal in einer temporären Tabelle ablegen und diese dann nutzen?
> >
> > Nein, eine Zwischenlagerung fällt aus,
>
> Mit temporärer Tabelle meinte ich den im System enthaltenen Mechanismus, eine solche Tabelle für die aktuelle Sitzung zu erstellen, die dann am Ende von selbst wieder verschwindet.
Ich weiß nicht so recht, ob das sinnvoll ist. Ich habe bisher einfach noch keinen Anwendungsfall für temporäre Tabelle gehabt. Daher kann ich die Vor- und Nachteile bezüglich Performanz und Komplexität überhaupt nicht einschätzen.
Wenn ich mit PHP den Query zur Erstellung einer temporären Tabelle absetze, bleibt die Tabelle bis zum schließen der Verbindung vorhanden?
> Vielleicht kann man noch das LEFT JOIN entsorgen und stattdessen den Wert user_name (ist ja anscheinend nur dieser eine) per correlated Subquery in der Select-Klausel ermitteln. Das nimmt ein Stück Komplexität aus der FROM-Klausel raus, und diese Tabelle wird für das Erstellen der Zwischenergebnismenge nicht benötigt.
Da der Name aus der Eintrags- **oder** der Benutzertabelle kommen kann, habe ich das mit einem `IF()` gelöst. Leider wird die „user_id“, so sie nicht vorhanden ist, mit einer „0“ statt mit `NULL` abgespeichert. Daher kommt der Vergleich mit `ent2.user_id > 0`. Die „user_id“ fällt dafür aus dem Abfrageergebnis heraus.
Dafür kommt nun das Feld „edited“ mit einem Zeitstempel oder `NULL` als Beweis für einen geänderten Eintrag hinzu. Zudem kommt ein zweites `IF()` hinzu, mit dem zwischen den Zeitstempeln für den Eintrag bzw. der letzten Bearbeitung gewählt wird. Mit diesem wird die gesamte Ergebnismenge sortiert.
Das alles lässt die Ausführungszeit für den Query beim ersten Aufruf zwar etwa auf nicht ganz das Doppelte der vorher vorhandenen Lösung steigen, aber danach, aus dem Cache, ist der Query so schnell, wie gehabt.
~~~sql
SELECT
ent1.id,
(IF(ent2.user_id > 0, (SELECT user_name FROM userdata WHERE userdata.user_id = ent2.user_id), ent2.name)) AS name,
ent2.time,
ent2.edited,
ent2.subject,
ent2.category,
(IF(ent2.edited IS NOT NULL, ent2.edited, entries.time)) AS orderTime
FROM ((
SELECT id FROM entries WHERE spam = 0 ORDER BY time DESC LIMIT 6)
UNION (
SELECT id FROM entries WHERE spam = 0 ORDER BY edited DESC LIMIT 6))
AS ent1
INNER JOIN entries AS ent2
ON ent1.id = ent2.id
ORDER BY orderTime DESC
LIMIT 6
~~~
Tschö, Auge
--
Wenn man ausreichende Vorsichtsmaßnahmen trifft, muss man keine Vorsichtsmaßnahmen mehr treffen.
Toller Dampf voraus von Terry Pratchett
UNION in IN() als Teil einer WHERE-Klausel? Zwischenstand.
bearbeitet von AugeHallo
> > > Muss es unbedingt eine einzelne Monster-Query sein, oder kannst du das Union-Ergebnis erstmal in einer temporären Tabelle ablegen und diese dann nutzen?
> >
> > Nein, eine Zwischenlagerung fällt aus,
>
> Mit temporärer Tabelle meinte ich den im System enthaltenen Mechanismus, eine solche Tabelle für die aktuelle Sitzung zu erstellen, die dann am Ende von selbst wieder verschwindet.
Ich weiß nicht so recht, ob das sinnvoll ist. Ich habe bisher einfach noch keinen Anwednungsfall für temporäre Tabelle gehabt. Daher kann ich die Vor- und Nachteile bezüglich Performanz und Komplexität überhaupt nicht einschätzen.
Wenn ich mit PHP den Query zur Erstellung einer temporären Tabelle absetze, bleibt die Tabelle bis zum schließen der Verbindung vorhanden?
> Vielleicht kann man noch das LEFT JOIN entsorgen und stattdessen den Wert user_name (ist ja anscheinend nur dieser eine) per correlated Subquery in der Select-Klausel ermitteln. Das nimmt ein Stück Komplexität aus der FROM-Klausel raus, und diese Tabelle wird für das Erstellen der Zwischenergebnismenge nicht benötigt.
Da der Name aus der Eintrags- **oder** der Benutzertabelle kommen kann, habe ich das mit einem `IF()` gelöst. Leider wird die „user_id“, so sie nicht vorhanden ist, mit einer „0“ statt mit `NULL` abgespeichert. Daher kommt der Vergleich mit `ent2.user_id > 0`. Die „user_id“ fällt auch aus dem Ergebnis heraus.
Dafür kommt nun das Feld „edited“ mit einem Zeitstempel oder `NULL` als Beweis für einen geänderten Eintrag hinzu. Zudem kommt ein zweites `IF()` hinzu, mit dem zwischen den Zeitstempeln für den Eintrag bzw. der letzten Bearbeitung gewählt wird. Mit diesem wird die gesamte Ergebnismenge sortiert.
Das alles lässt die Ausführungszeit für den Query beim ersten Aufruf zwar etwa auf nicht ganz das Doppelte der vorher vorhandenen Lösung steigen, aber danach, aus dem Cache, ist der Query so schnell, wie gehabt.
~~~sql
SELECT
ent1.id,
(IF(ent2.user_id > 0, (SELECT user_name FROM userdata WHERE userdata.user_id = ent2.user_id), ent2.name)) AS name,
ent2.time,
ent2.edited,
ent2.subject,
ent2.category,
(IF(ent2.edited IS NOT NULL, ent2.edited, entries.time)) AS orderTime
FROM ((
SELECT id FROM entries WHERE spam = 0 ORDER BY time DESC LIMIT 6)
UNION (
SELECT id FROM entries WHERE spam = 0 ORDER BY edited DESC LIMIT 6))
AS ent1
INNER JOIN entries AS ent2
ON ent1.id = ent2.id
ORDER BY orderTime DESC
LIMIT 6
~~~
Tschö, Auge
--
Wenn man ausreichende Vorsichtsmaßnahmen trifft, muss man keine Vorsichtsmaßnahmen mehr treffen.
Toller Dampf voraus von Terry Pratchett