La réponse actuellement acceptée ne répond pas à la question. Et c'est faux dans le principe. a BETWEEN x AND y
se traduit par :
a >= x AND a <= y
Y compris la limite supérieure, alors que les gens doivent généralement exclure il :
a >= x AND a < y
Avec dates vous pouvez facilement régler. Pour l'année 2009, utilisez '2009-12-31' comme limite supérieure.
Mais ce n'est pas aussi simple avec les horodatages qui autorisent les chiffres fractionnaires. Les versions modernes de Postgres utilisent un entier de 8 octets en interne pour stocker jusqu'à 6 secondes fractionnaires (résolution en µs). Sachant cela, nous pourrions le faire fonctionner, mais ce n'est pas intuitif et dépend d'un détail d'implémentation. Mauvaise idée.
De plus, a BETWEEN x AND y
ne trouve pas les plages qui se chevauchent. Nous avons besoin :
b >= x AND a < y
Et des joueurs qui ne sont jamais partis ne sont pas encore pris en compte.
Bonne réponse
En supposant l'année 2009
, je reformule la question sans en changer le sens :
"Rechercher tous les joueurs d'une équipe donnée qui ont rejoint avant 2010 et n'ont pas quitté avant 2009."
Requête de base :
SELECT p.*
FROM team t
JOIN contract c USING (name_team)
JOIN player p USING (name_player)
WHERE t.name_team = ?
AND c.date_join < date '2010-01-01'
AND c.date_leave >= date '2009-01-01';
Mais il y a plus :
Si l'intégrité référentielle est appliquée avec des contraintes FK, la table team
lui-même n'est que du bruit dans la requête et peut être supprimé.
Bien qu'un même joueur puisse quitter et rejoindre la même équipe, nous devons également plier d'éventuels doublons, par exemple avec DISTINCT
.
Et nous pourrons faut prévoir un cas particulier :les joueurs qui ne sont jamais partis. En supposant que ces joueurs ont NULL dans date_leave
.
"Un joueur dont on ne sait pas qu'il est parti est supposé jouer pour l'équipe à ce jour."
Requête affinée :
SELECT DISTINCT p.*
FROM contract c
JOIN player p USING (name_player)
WHERE c.name_team = ?
AND c.date_join < date '2010-01-01'
AND (c.date_leave >= date '2009-01-01' OR c.date_leave IS NULL);
La priorité des opérateurs joue contre nous, AND
se lie avant OR
. Nous avons besoin de parenthèses.
Réponse associée avec DISTINCT
optimisé (si les doublons sont fréquents) :
- Tableau plusieurs à plusieurs – Les performances sont mauvaises
Généralement, les noms des personnes physiques ne sont pas uniques et une clé primaire de substitution est utilisée. Mais, évidemment, name_player
est la clé primaire du player
. Si vous n'avez besoin que des noms de joueurs, nous n'avons pas besoin de la table player
dans la requête, soit :
SELECT DISTINCT name_player
FROM contract
WHERE name_team = ?
AND date_join < date '2010-01-01'
AND (date_leave >= date '2009-01-01' OR date_leave IS NULL);
SQL OVERLAPS
opérateur
Le manuel :
OVERLAPS
prend automatiquement la valeur la plus ancienne de la paire comme début. Chaque période est considérée comme représentant l'intervalle de demi-ouverturestart <= time < end
, sauf sistart
etend
sont égaux auquel cas il représente cet instant unique.
Pour prendre soin des potentiels NULL
valeurs, COALESCE
semble le plus simple :
SELECT DISTINCT name_player
FROM contract
WHERE name_team = ?
AND (date_join, COALESCE(date_leave, CURRENT_DATE)) OVERLAPS
(date '2009-01-01', date '2010-01-01'); -- upper bound excluded
Type de plage avec prise en charge d'index
Dans Postgres 9.2 ou version ultérieure vous pouvez également opérer avec des types de plage réels :
SELECT DISTINCT name_player
FROM contract
WHERE name_team = ?
AND daterange(date_join, date_leave) &&
daterange '[2009-01-01,2010-01-01)'; -- upper bound excluded
Les types de plage ajoutent des frais généraux et occupent plus d'espace. 2 x date
=8 octets ; 1 x daterange
=14 octets sur disque ou 17 octets en RAM. Mais en combinaison avec l'opérateur de chevauchement &&
la requête peut être prise en charge avec un index GiST.
En outre, pas besoin de valeurs NULL spéciales. NULL signifie "plage ouverte" dans un type de plage - exactement ce dont nous avons besoin. La définition de la table n'a même pas besoin de changer :nous pouvons créer le type de plage à la volée - et prendre en charge la requête avec un index d'expression correspondant :
CREATE INDEX mv_stock_dr_idx ON mv_stock USING gist (daterange(date_join, date_leave));
Connexe :
- Tableau d'historique des stocks moyens