Aktuálně přijímaná odpověď na otázku neodpovídá. A v principu je to špatně. a BETWEEN x AND y překládá do:
a >= x AND a <= y Včetně horní mez, zatímco lidé obvykle potřebují vyloučit to:
a >= x AND a < y
S daty můžete snadno upravit. Pro rok 2009 použijte jako horní hranici '2009-12-31'.
Ale s časovými razítky to není tak jednoduché které umožňují zlomkové číslice. Moderní verze Postgresu používají interně 8bajtové celé číslo k uložení až 6 zlomkových sekund (rozlišení µs). Když to víme, mohli bychom stále to funguje, ale to není intuitivní a závisí na detailu implementace. Špatný nápad.
Navíc a BETWEEN x AND y nenajde překrývající se rozsahy. Potřebujeme:
b >= x AND a < y A hráči, kteří nikdy neodešli zatím nejsou zvažovány.
Správná odpověď
Za předpokladu roku 2009 , přeformuluji otázku, aniž bych změnil její význam:
"Najděte všechny hráče daného týmu, kteří se připojili před rokem 2010 a neodešli před rokem 2009."
Základní dotaz:
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';
Ale je toho víc:
Pokud je referenční integrita vynucena s omezeními FK, tabulka team sám o sobě je pouze šum v dotazu a lze jej odstranit.
I když stejný hráč může odejít a znovu se připojit ke stejnému týmu, musíme také poskládat možné duplikáty, například pomocí DISTINCT .
A my můžeme potřeba zajistit speciální případ:hráče, kteří nikdy neodešli. Za předpokladu, že tito hráči mají v date_leave hodnotu NULL .
"Předpokládá se, že hráč, o kterém není známo, že odešel, hraje za tým dodnes."
Upřesněný dotaz:
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);
Přednost operátora jde proti nám, AND se váže před OR . Potřebujeme závorky.
Související odpověď s optimalizovaným DISTINCT (pokud jsou duplikáty běžné):
- Tabulka mnoho k mnoha – výkon je špatný
Obvykle jména fyzických osob nejsou jedinečné a používá se náhradní primární klíč. Ale samozřejmě name_player je primární klíč player . Pokud potřebujete pouze jména hráčů, nepotřebujeme tabulku player v dotazu buď:
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 operátor
Manuál:
OVERLAPSautomaticky vezme dřívější hodnotu páru jako start. Každé časové období je považováno za polovinu otevřeného intervalustart <= time < end, pokudstartaendjsou stejné, v takovém případě představuje tento jediný časový okamžik.
Postarat se o potenciální NULL hodnoty, COALESCE zdá se nejjednodušší:
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
Typ rozsahu s podporou indexu
V Postgres 9.2 nebo novější můžete také pracovat se skutečnými typy rozsahů :
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
Typy rozsahů zvyšují režii a zabírají více místa. 2 x date =8 bajtů; 1 x daterange =14 bajtů na disku nebo 17 bajtů v RAM. Ale v kombinaci s operátorem překrytí && dotaz může být podporován indexem GiST.
Také není třeba zadávat hodnoty NULL se speciálními případy. NULL znamená "otevřený rozsah" v typu rozsahu - přesně to, co potřebujeme. Definice tabulky se ani nemusí měnit:můžeme vytvořit typ rozsahu za běhu – a podpořit dotaz s odpovídajícím indexem výrazu:
CREATE INDEX mv_stock_dr_idx ON mv_stock USING gist (daterange(date_join, date_leave));
Související:
- Tabulka průměrné historie akcií