MySQL: UNION ignoriert ORDER BY - Was kann ich tun?
Hallo, ich habe ein Problem mit meinem MySQL Select Statement.
Folgendes Szenario: Ich möchte alle location_tasks auflisten, dabei sollen die Tasks, bei denen die job_id = 4 ist als erstes aufgelistet werden. Danach sollen alle anderen Tasks folgen, bei denen die job_id != 4 ist. Diese sollen aber nach dem job_level sortiert werden.
SELECT *
FROM location_task, job
WHERE location_task_location_id = 0
AND location_task_job = job_id
UNION
(SELECT *
FROM location_task, job
WHERE location_task_location_id = 0
AND location_task_job = job_id
AND job_id != 4
ORDER BY job_level)
Das Problem: Die beiden Selects funktionieren alleine wunderbar, doch durch das UNION wird der ORDER BY-Teil ignoriert. Wenn ich die Klammern weg lasse, werden alle Tasks nach dem job_level sortiert, sodass diejenigen mit der job_id = 4 nicht als erstes angezeigt werden.
Kann hier jemand weiterhelfen?
5 Antworten
Eine einfach zu durchschauende Möglichkeit zum Beeinflussen der Sortierung bei der Verwendung von UNION besteht darin, bei jedem SELECT eine Konstante voranzustellen. Wenn der erste SELECT z.B. eine 1, der zweite eine 2 bekommt und man sortiert vorrangig nach der Konstanten, so kommen alle Sätze des ersten SELECT nach vorne, die des zweiten nach hinten, egal was in den Sätzen steht.
Die ORDER BY -Anweisung darf nur einmal am Schluss stehen und darf nur Spalten-Nrn enthalten, da die Feldnamen bei den einzelnen SELECTs nicht zwangsläufig übereinstimmen müssen. Wenn Du also schreibst
ORDER BY 1, 7
so wird zuerst nach der vorangestellten Konstante und dann nach der 7. Spalte sortiert (die Spalte, wo Dein Sortierkriterium steht, die Konstante vorne natürlich mitgerechnet). Die Konstante muss nicht unbedingt vorne stehen. Es kann sein, dass der Stern Probleme macht und qualifiziert werden muss:
SELECT 1, location_task.*, job.* FROM location_task, job ....
UNION
SELECT 2, location_task.*, job.* FROM location_task, job ....
So geht es auf jeden Fall.
Jetzt musst Du noch die WHERE-Bedingungen so setzen, dass jeder SELECT die Sätze für die zugedachte Reihenfolge bekommt und dass keine Sätze mehrfach selektiert werden.
Ich habe öfter solche Abfragen und man bekommt so die raffiniertesten Reihenfolgen hin.
mein vorschlag:
select (jobid = 4) as job4, jobid, etc
FROM location_task, job WHERE location_task_location_id = 0 AND location_task_job = job_id
order by job4 desc, jobid
dann hast du lediglich ein neues feld job4 im datenarray, was du auf php-ebene einfach ignorierst.
(etc sind deine restlichen felder)
Perfekt. Funktioniert wie es soll. Danke :)
Hab die Lösung mal etwas angepasst:
SELECT *, (job_id = 4) AS job_id_selected FROM location_task, job WHERE location_task_location_id = 0 AND location_task_job = job_id ORDER BY job_id_selected DESC, job_level ASC
Union macht aus zwei Datensätzen einen großen. Es ist kein "zusammenkleben" von Tabellenzeilen, sondern eine echte Fusion auf Inhaltsebene. Deshalb ist für ein Union-Statement auch nur ein einziges "order by" ganz am Ende zulässig. Die einzelnen Selects dürfen nicht sortieren.
Um die Sortierung so hinzubiegen, dass "4" vor "0" kommt, kannst du ein Case-Statement verwenden und die "4" auf "-99" abbilden:
https://dev.mysql.com/doc/refman/5.0/en/case.html
SELECT * FROM
(
SELECT
CASE job_id
WHEN 4 THEN -99
ELSE job_id
END CASE as SORT_JOB_ID,
location_task.*, job.*
FROM location_task, job
WHERE location_task_location_id = 0
AND location_task_job = job_id
)
ORDER BY SORT_JOB_ID
Jetzt musst du im Handbuch deiner MySQL-Version nachschlagen. Wahrscheinlich mag er das "as" nicht, probier es mal ohne.
Oh. Jetzt verstehe ich auch, was genau du vor hast. Leider geht das nicht, da die Daten in php verarbeitet werden und leider nicht verfälscht werden dürfen :/
Da wird auch nichts verfälscht. Das Sortierfeld wird zusätzlich selektiert, neben den normalen Feldern. Dein PHP-Skript muss es nicht mit ausgeben.
Das order by muss ausserhalb der Klammer für das select stehen
SELECT * FROM location_task, job WHERE location_task_location_id = 0 AND location_task_job = job_id UNION (SELECT * FROM location_task, job WHERE location_task_location_id = 0 AND location_task_job = job_id AND job_id != 4) ORDER BY job_level;
Sorry hab überlesen dass alle mit Job id 4 als erstes gezeigt werden sollen.
Musst du nicht im ersten Select noch ein
AND job_id = 4
anhängen?
Hatte ich beim kopieren irgendwie gelöscht. Hatte ich aber mit benutzt. Ändert nichts an der Tatsache ;)
Klingt gut. Ich bekomme aber leider noch einen Error: