Petite mise en situation...
Vous êtes l'administrateur d'une base de données relativement conséquente comportant de nombreuses tables traitant de différents sujets sous différentes formes, et vous souhaiteriez vous créer un petit flux indiquant les dernières mises à jour en temps réel ? (Toute référence à un webmaster existant ou ayant existé est purement fortuite).

La première solution consisterait à faire autant de requêtes que vous avez de tables, et à comparer "manuellement" chacun des résultats. Long, peu efficace et difficile à maintenir.
En fait, SQL offre une solution simple et agréable, mais peu connue pour ce genre de situations. Mesdames et messieurs, je vous demande de faire un tonnerre d'applaudissements pour UNION !

Problématique

Vous savez peut être faire une jointure SQL : cette opération puissante et relativement simple permet de "coller" des données dans vos résultats. Cependant, l'ajout ne crée pas de "nouvelles lignes" (en tout cas, pas dans le sens où nous l'entendons, puisque pour un attribut donné l'ensemble pris par les valeurs reste le même) ; l'opération se contentant d'ajouter des colonnes supplémentaires : en fait, il s'agit d'une insertion de données à la verticale. La jointure n'étant pas du tout le but de cet article, je vous laisse vous référer à des liens plus approfondis pour une explication concrète.

Dans notre cas, nous serions plus intéressés par un ajout de données à l'horizontale : autrement dit, une sorte de mélange entre deux tables. La norme SQL prévoie cela, mais l'implémentation est assez peu connue...

Syntaxe UNION

Le mot clé UNION permet de joindre deux résultats de requêtes (ou plus), sous certaines conditions :

  • Le nombre d'attributs (de colonnes) pour chaque SELECT doit être le même (logique !). Cela signifie que si vos tables ont des structures complètement distinctes, il va falloir jouer du CONCAT et autres AS pour homogénéiser vos données.
  • Chaque requête doit renvoyer le même type de données. Encore une fois, rien de bien étonnant : il est difficile d'avoir un ENUM et du TEXT sur une même colonne !
  • Par défaut, les requêtes ne doivent pas utiliser d'ORDER BY. Ce n'est pas vraiment un problème, puisqu'il suffit d'un requête imbriquée pour éviter cela.

Un exemple

Supposons que nous ayons trois tables, qui stockent la date et des actions. Le but de l'exercice va être d'afficher les dix dernières actions, en fonction de la date, toutes tables confondues.

Afin de faire simple et concis, nous allons prendre deux colonnes : une qui indiquera la date et l'autre la modification (nous leur donnerons les alias Date et Texte). Dans la réalité, vous aurez sûrement besoin de complexifier cela...
Note : les dates sont au format DATE sous SQL, mais nous allons récupérer un timestamp afin de pouvoir l'afficher au format français j/m/A. Pour cela, il suffit d'appliquer la fonction mySQL UNIX_TIMESTAMP (si vous n'avez pas besoin de traiter les données côté serveur, un DATE_FROMAT fera ça plus rapidement).

  1. Première table : prenons par exemple un blog Joomla dont nous souhaitons afficher les dernières modifs. Il n'y a rien de compliqué ici :

    SELECT UNIX_TIMESTAMP(modified) as Date, title AS Texte
    FROM blog_content
    
  2. Complexifions un peu : nous allons maintenant utiliser CONCAT pour afficher les infos d'une table dont la structure contient un nom de projet et la modification apportée au projet :

    SELECT UNIX_TIMESTAMP(Date) AS Date,CONCAT("{",Projet,"} ",Modification) AS Texte
    FROM TimeLine
    
  3. Enfin, complexifions tout cela avec une dernière requête répartie sur différentes tables (BDDR).

    SELECT UNIX_TIMESTAMP(Date) AS Date,CONCAT(Titre, " : ",Modification," par ", Auteurs.Auteur) AS Texte
    FROM Modifs
    LEFT JOIN Articles ON (Articles.ID=Modifs.Reference)
    LEFT JOIN Auteurs ON (Auteurs.ID=Modifs.Auteur)
    

Voilà donc les trois requêtes que nous aurions dû exécuter séparément, et qu'UNION va "coller" pour nous. Dernière chose à faire : penser à trier en fonction de la date décroissante (les événements les plus récents en premier). Dans ce cas, j'ajoute aussi une colonne Type qui permet de voir d'un seul coup d'œil d'où provient la modification.

SELECT UNIX_TIMESTAMP(Date) AS Date,CONCAT(Titre, " : ",Modification," par ", Auteurs.Auteur) AS Texte,"Article" AS Type
FROM Modifs
LEFT JOIN Articles ON (Articles.ID=Modifs.Reference)
LEFT JOIN Auteurs ON (Auteurs.ID=Modifs.Auteur)

UNION

SELECT UNIX_TIMESTAMP(modified) as Date, title AS Texte,"Blog" AS Type
FROM zBLOG_content

UNION

SELECT UNIX_TIMESTAMP(Date) AS Date,CONCAT("{",Projet,"} ",Modification) AS Texte,"TL" AS Type
FROM _TimeLine

ORDER BY DATE DESC
LIMIT 10

Et pour afficher les résultats (PHP) :

echo '
    ' . "\n"; while($Log=mysql_fetch_assoc($LogsSQL)) echo '
  1. [' . $Log['Type'] . ' | ' . date('d/m/y à G:i:s',$Log['Date']) . '] '. $Log['Texte'] . '
  2. '; echo '
' . "\n";

Tadaaam ! Les curieux pourront voir ce que cela donne sur cette page [EDIT : page supprimée], taillée pour un affichage sur mobile (code le plus concis possible).