Chaînes et dates, moteur par moteur

Les fonctions de texte et de date varient beaucoup selon les moteurs, dans leur nom comme dans leur comportement. Cette leçon compare la syntaxe nécessaire pour obtenir le même résultat.

Assembler du texte

1

SELECT first_name || ' ' || last_name AS customer,
       country
FROM customers
LIMIT 10;
Ouvrir dans Planche

Résultat — 5 premières lignes sur 10

customercountry
Liam FischerES
Mateo CostaFR
Yuki JangDE
Ines MeyerAU
Leo NovakES

Planche s’ouvre dans un onglet séparé, la base d’exemple déjà chargée et cette requête prête dans l’éditeur. Tous les exemples de la page réutilisent cet onglet, et rien ne s’exécute tant que vous n’appuyez pas sur F9.

The same expression elsewhere

-- PostgreSQL, Oracle: identical to SQLite.
SELECT first_name || ' ' || last_name AS customer FROM customers;

-- MySQL / MariaDB: || means OR unless PIPES_AS_CONCAT is set.
SELECT CONCAT(first_name, ' ', last_name) AS customer FROM customers;

-- SQL Server: + concatenates, and CONCAT() exists from 2012.
SELECT first_name + ' ' + last_name AS customer FROM customers;
SELECT CONCAT(first_name, ' ', last_name) AS customer FROM customers;

The same expression elsewhere — donné à titre de comparaison. La base d’exemple du navigateur est SQLite, cette instruction ne peut donc pas s’exécuter ici.

L’opérateur || est celui du standard, et PostgreSQL, Oracle et SQLite l’utilisent. MySQL donne à || son propre sens — le OU logique — sauf si le serveur tourne avec PIPES_AS_CONCAT : sous MySQL, l’expression n’échoue donc pas, elle renvoie 0 ou 1. SQL Server utilise +, ambigu dans l’autre sens : avec deux colonnes numériques, il additionne.

CONCAT() existe sur MySQL, MariaDB, PostgreSQL et SQL Server (2012 et versions ultérieures). C’est donc la forme la plus portable entre ces quatre moteurs. Son comportement face à NULL diffère toutefois de celui des opérateurs.

Deux comportements diffèrent, pas seulement l’orthographe :

  • NULL se propage à travers les opérateurs. 'a' || NULL vaut NULL sous PostgreSQL et SQLite, et 'a' + NULL vaut NULL sous SQL Server. Un seul deuxième prénom manquant vide donc toute la colonne. CONCAT(), lui, traite NULL comme une chaîne vide. Le || d’Oracle ignore aussi NULL, à cause du point suivant.
  • Oracle traite la chaîne vide comme NULL. Là-bas, '' IS NULL est vrai, et faux partout ailleurs : un WHERE name = '' qui trouve des lignes sous MySQL n’en trouve aucune sous Oracle.

Formater une date

3

SELECT order_number,
       ordered_at,
       strftime('%Y-%m', ordered_at) AS month,
       strftime('%d/%m/%Y', ordered_at) AS day_first
FROM orders
LIMIT 10;
Ouvrir dans Planche

Résultat — 5 premières lignes sur 10

order_numberordered_atmonthday_first
ORD-2022-0000012022-03-09 16:12:522022-0309/03/2022
ORD-2023-0000022023-12-13 00:59:502023-1213/12/2023
ORD-2024-0000032024-06-01 20:22:142024-0601/06/2024
ORD-2023-0000042023-04-12 22:22:302023-0412/04/2023
ORD-2025-0000052025-07-15 11:11:562025-0715/07/2025

Formatting a date elsewhere

-- MySQL / MariaDB
SELECT DATE_FORMAT(ordered_at, '%Y-%m') AS month FROM orders;

-- PostgreSQL, Oracle
SELECT to_char(ordered_at, 'YYYY-MM') AS month FROM orders;

-- SQL Server (2012+)
SELECT FORMAT(ordered_at, 'yyyy-MM') AS month FROM orders;
-- ...or, much faster in bulk, and the usual choice for grouping:
SELECT CONVERT(char(7), ordered_at, 126) AS month FROM orders;

Formatting a date elsewhere — donné à titre de comparaison. La base d’exemple du navigateur est SQLite, cette instruction ne peut donc pas s’exécuter ici.

Quatre noms de fonctions différents et trois langages de motifs différents. strftime utilise le %Y-%m de style C, DATE_FORMAT les mêmes codes en pourcentage avec des sens différents pour certaines lettres, to_char utilise YYYY-MM, et FORMAT le yyyy-MM de .NET — où la casse compte, puisque mm désigne les minutes.

Pour regrouper plutôt que pour afficher, choisissez une forme peu coûteuse à calculer. Sous SQL Server, CONVERT(char(7), col, 126) est nettement plus rapide que FORMAT sur de nombreuses lignes. Sous PostgreSQL, date_trunc('month', col) est généralement préférable à to_char et conserve un type date. Enfin, une fonction appliquée à la colonne empêche souvent l’utilisation de son index.

Planche normalise ce qui revient, quel que soit le moteur qui l’a produit. Les pilotes affichent les dates au format YYYY-MM-DD HH:MM:SS[.mmm] pour que la grille reste cohérente, et le pilote Oracle règle NLS_DATE_FORMAT en conséquence : un TO_CHAR que vous écrivez vous-même s’accorde donc avec ce que la grille affiche à côté.

L’écart entre deux dates

5

SELECT o.order_number,
       o.ordered_at,
       s.delivered_at,
       ROUND(julianday(s.delivered_at) - julianday(o.ordered_at), 1) AS days_to_deliver
FROM orders o
JOIN shipments s ON s.order_id = o.id
WHERE s.delivered_at IS NOT NULL
ORDER BY days_to_deliver DESC
LIMIT 15;
Ouvrir dans Planche

Résultat — 5 premières lignes sur 15

order_numberordered_atdelivered_atdays_to_deliver
ORD-2023-0001522023-11-27 00:00:562023-12-07 22:33:0010.9
ORD-2025-0012882025-10-18 04:20:052025-10-28 19:54:5610.6
ORD-2024-0018162024-01-23 03:41:562024-02-02 18:40:5210.6
ORD-2023-0029762023-05-23 02:17:372023-06-02 15:33:5310.6
ORD-2022-0013452022-05-02 07:54:462022-05-12 19:12:5610.5

Days between two dates elsewhere

-- MySQL / MariaDB: whole days, first argument minus second.
SELECT DATEDIFF(delivered_at, ordered_at) AS days FROM ...;

-- PostgreSQL: subtracting two timestamps gives an interval.
SELECT delivered_at - ordered_at AS elapsed FROM ...;
SELECT EXTRACT(DAY FROM delivered_at - ordered_at) AS days FROM ...;

-- SQL Server: the unit comes first, and the arguments are the other way round.
SELECT DATEDIFF(day, ordered_at, delivered_at) AS days FROM ...;

-- Oracle: subtracting two DATEs gives a number of days.
SELECT delivered_at - ordered_at AS days FROM ...;

Days between two dates elsewhere — donné à titre de comparaison. La base d’exemple du navigateur est SQLite, cette instruction ne peut donc pas s’exécuter ici.

Ces fonctions ne diffèrent pas seulement par leur nom. Le DATEDIFF(a, b) de MySQL prend deux arguments et renvoie a - b en jours entiers. Le DATEDIFF(day, a, b) de SQL Server prend d’abord l’unité et renvoie b - a. L’ordre des arguments est donc inversé : une requête copiée de l’un à l’autre peut produire un résultat de signe opposé. PostgreSQL et Oracle utilisent la soustraction, qui renvoie respectivement un intervalle et un nombre de jours.

La version de SQL Server compte en outre les frontières franchies, et non le temps écoulé : DATEDIFF(day, '2025-01-01 23:00', '2025-01-02 01:00') vaut 1, pour deux heures d’écart.

Remplacer un NULL

7

SELECT id,
       first_name,
       phone,
       COALESCE(phone, '(none)') AS portable,
       IFNULL(phone, '(none)')   AS sqlite_and_mysql
FROM customers
WHERE phone IS NULL
LIMIT 10;
Ouvrir dans Planche

Résultat — 5 premières lignes sur 10

idfirst_namephoneportablesqlite_and_mysql
2MateoNULL(none)(none)
9SvenNULL(none)(none)
21LaylaNULL(none)(none)
25FreyaNULL(none)(none)
28PriyaNULL(none)(none)

Replacing a NULL elsewhere

-- Everywhere, and the one to reach for:
SELECT COALESCE(phone, '(none)') FROM customers;

-- MySQL / MariaDB / SQLite
SELECT IFNULL(phone, '(none)') FROM customers;

-- SQL Server. Note ISNULL, not IFNULL, and it takes exactly two arguments.
SELECT ISNULL(phone, '(none)') FROM customers;

-- Oracle
SELECT NVL(phone, '(none)') FROM customers;

Replacing a NULL elsewhere — donné à titre de comparaison. La base d’exemple du navigateur est SQLite, cette instruction ne peut donc pas s’exécuter ici.

COALESCE est standard, existe partout, et prend autant d’arguments qu’on veut — il renvoie le premier qui n’est pas NULL. Les variantes propres à chaque moteur en prennent exactement deux, et deux de ces noms se ressemblent assez pour qu’on les intervertisse : MySQL et SQLite ont IFNULL, SQL Server a ISNULL. Il n’y a aucune raison de les préférer à COALESCE dans du code neuf.

En bref

Si une requête doit tourner sur plus d’un moteur, CONCAT(), COALESCE() et CAST() couvrent l’essentiel. Le formatage des dates ne se transporte pas, et se fait généralement mieux dans l’application, où vous avez de toute façon la langue et le fuseau horaire de l’utilisateur.