Problem
Es ist ja lobenswert, daß Strato relativ schnell die MySQL-Versionen aktualisiert, vor ein paar Tagen von 5.0.45 auf 5.0.51. Man sollte auch denken, daß es dadurch mit Applikationen keine Probleme geben dürfte. Nich so diesmal, denn plötzlich stimmte bei so manchen WordPressinstalltionen die Sortierreihenfolge der Beiträge nicht mehr, wie mehrere Nutzer im WP-Deutschlanforum beklagten. Aber nicht nur Strato war davon betroffen, auch bei 1&1 und anderen Hostern gab es Probleme.
Normalerweise kommen ja die neuesten Artikel zuerst, es wird also absteigen nach Datum sortiert. Nun waren aber plötzlich die ältesten Beiträge ganz oben, als scheinbar eine umgekehrte, also aufsteigende Sortierung. Und niemand hatte bewußt etwas verändert, also keine Plugins installiert oder die Themedateien bzw. die Konfiguration angepaßt. Also konnte es nur am Hoster liegen. Da ich hier bei Schnurpsel diesen Effekt nicht beobachten konnte, dachte ich zunächst doch an irgendein Plugin, bis ich mal spaßeshalber mein WordPress-2.0.x-Testbolg aufgrufen hatte. Und siehe da, auch hier waren die Beiträge plötzlich nicht mehr in der gewünschten Reihenfolge. Da ich dort aber wirklich seit Monaten nichts verändert hatte und nur ein paar eigene Plugins verwende, mußte es doch irgendeine andere Ursache haben. Zumindest hatte ich jetzt die Chance, der Sache auf den Grund zu gehen.
Hintergrund
Meine erster Schritt war, mir mal die von WP generierte SQL-Abfrage anzusehen. Meine Vermutung war zunächst, daß irgendwo die Sortierung (ORDER BY post_date) verloren geht. So sieht die SQL-Abfrage aus:
SELECT DISTINCT *
FROM wp20_posts
WHERE 1 =1
AND post_date_gmt <= '2008-01-11 22:03:59'
AND (
post_status = 'publish'
OR post_author =1
AND post_status != 'draft'
AND post_status != 'static'
)
AND post_status != 'attachment'
GROUP BY wp20_posts.ID
ORDER BY post_date DESC
LIMIT 0 , 10
Vom komischen WHERE 1=1 mal abgesehen gab es zumindest keine Auffälligkeiten, die Sortierung ist auch drin aber was soll bitte das GROUP BY wp20_posts.ID? Das Feld ID ist ein Autoinkrement-Feld, also immer eindeutig (UNIQUE) und somit kann es da nie zwei- oder mehrmals den selben Wert geben, also kann auch nichts gruppiert werden. Mit GROUP BY feldname werden normalerweise Datensätze zusammengefaßt, die in dem oder den angegebenen Feldern die selben Werte enthalten. Ein Blick in den Quelltext gibt dann Aufschluß, dieses GROUP BY ist beim Zusammensetzen des SQL-Strings fest codiert und muß daher immer belegt werden, damit die Abfrage syntaktisch richtig ist:
"SELECT $distinct * FROM $wpdb->posts $join WHERE 1=1" . $where . " GROUP BY " . $groupby . " ORDER BY " . $orderby . " $limits";
Wenn keine echte Gruppierung verwendet wird, wird dafür einfach als Dummy-Wert das Feld ID eingesetzt. Normalerweise kein Probem, weil ja eben dadurch nichts gruppiert wird. Und genau deshalb wird sowas auch vom SQL-Server letztendlich wegoptimiert, daß heißt, es steht zwar da, kommt aber nicht zur Anwendung. Außerdem bewirkt ein GROUP BY auch implizit eine Sortierung nach den dort angegebenen Feldern. Und genau hier liegt das Problem.
Wurde bei MySQL vor Version 5.0.51 mit dem GROUP BY für eindeutige Felder auch die implizite Sortierung wegoptimiert, ist das in der neuen Version nun anders. Hier bleit diese Sortierung erhalten, auch wenn keine Gruppierung stattfindet. In den Release-Notes findet man den entsprechenden Hinweis zu diesem Fehler (30596).
Lösung
Betroffen ist, sofern nicht durch Plugins Gruppierungen hinzugefügt oder verändert werden, nur WordPress Version 2.0.x, zu WP 1.x kann ich nichts sagen. Bei WordPress ab Version 2.1 wurde das Verhalten geändert, so daß GROUP BY nur noch dann in den SQL-String eingebaut wird, wenn wirklich eine Gruppierung verwendet werden soll.
Nachtrag: Auch in WordPress ab Version 2.1 kann das Problem auftreten, wenn man etwas mit Kategorien oder Tags (ab 2.3) verändert, also z.B. nur bestimmte Kategorien in die Anzeige einbeziehen will:
<?php query_posts( $query_string.'&cat=1,2,3' ); ?>
Falls man keine spezielle Kategorie- oder Tagkonfiguration verwendet, ergibt sich auch direkt die erste Lösungsmöglichkeit, nämlich auf die neuste WordPress-Version (derzeit 2.3.2) zu aktualisieren.
Falls man das aus irgendeinem Grund nicht möchte oder das Kategorie-/Tagproblem auftritt, ist die zweite Lösung ein Plugin, welches das GROUP BY auf einen nicht störenden Wert zurechtbiegt.
Als WordPress-Plugin sehen die paar Zeilen PHP-Code dann so aus:
<?php
/*
Plugin Name: 123 No Group By ID
Plugin URI: http://schnurpsel.de/wordpress-und-die-suboptimale-mysql-optimierung-5051-74/
Description: Ändert bei WP das GROUP BY id in GROUP BY post_date (Problem ab MySQL 5.0.51).
Author: Ingo Henze
Version: 0.12
Author URI: http://putzlowitsch.de/
*/
// GROUP BY auswerten
function plw123ngb_posts_groupby( $groupby ) {
if( preg_match( "/(|[ ,.])id(|[ ,])/i", $groupby ) ) {
// sonst GROUP BY auf post_date setzen
$groupby = 'post_date';
}
return $groupby;
}
add_filter( 'posts_groupby', 'plw123ngb_posts_groupby' );
?>
Mann kann den Quelltext hier einfach rauskopieren, in einer Datei speichern, auf den Server in das Pluginverzeichnis kopieren und aktivieren. Oder man nimmt das fertige Plugin als ZIP-Datei.
Download: 123 No Group By ID 0.12
Sofern in der "GROUP BY"-Felderliste das Feld ID auftaucht, wird alles durch POST_DATE ersetzt. Das ist zwar so ein bißchen eine "Holzhammermethode", sollte aber in den meisten Fällen keine Nebenwirkungen zeigen. Probleme könnten nur dann auftreten, falls ein Plugin auch irgendwelche Gruppierungen vornimmt und dabei ebenfalls das Feld ID mit einbezieht. Zudem würde für den eher unwahrscheinlichen Fall, daß es zwei oder mehrere Beiträge mit exakt dem selben Veröffentlichungszeitpunkt gibt, nur einer von diesen angezeigt werden.