Upozornění na narozeniny v SQL
Pokud máme v databázi uloženo datum narození uživatele, tak můžeme chtít zjistit, jestli nebude v nejbližší době slavit narozeniny. Dalo by se to udělat tak, že od aktuálního data odečteme datum narození a pokud zbytek po dělení 365 dá nějaké číslo o málo menší než nula 365, tak bude mít brzo narozeniny. Problém s tímto přístupem spočívá v tom, že kvůli přestupným rokům nemá každý rok 365 dní, takže by výpočet nebyl přesný. Buď by se dal počet přestupných roků mezi narozením a aktuálním datem zohlednit (to by ale bylo velmi pracné), nebo se dá použít trik:
SELECT DATEDIFF(CONCAT(
YEAR(CURDATE()) + IF(SUBSTR(narozeni, 5) < SUBSTR(CURDATE(), 5), 1, 0),
SUBSTR(narozeni, 5)
), CURDATE()) AS narozeniny_za
FROM uzivatele
HAVING narozeniny_za BETWEEN 0 AND 14
Do data narození uživatele dosadíme aktuální rok (nebo ten příští, pokud už letos uživatel narozeniny měl) a odečteme od něj aktuální datum. Výsledné číslo je počet dní, za které bude mít uživatel narozeniny.
Funkce respektuje přestupné roky, jediný problém je s uživateli, kteří se narodili 29.2. Tito chudáci v nepřestupný rok narozeniny prostě nemají, takže se pro ně vrátí hodnota NULL.
Pro vyhodnocení dotazu se bohužel nepoužijí indexy. Pokud by se použít měly, bylo by nutné den, měsíc a rok ukládat zvlášť a podmínku celou přepsat.
Věk lze zjistit jednodušeji.
Diskuse
Promin, mozna jsem to vubec nepochopil ale neco jako tohle nefunguje?
SELECT DATEDIFF(narozeni, CURDATE()) where narozeni between DATE_ADD(CURDATE(), INTERVAL 31 DAY) AND CURDATE()
23.10.2009 02:48:45
<em>anonymní</em>:
Ne, to by vypsalo jen lidi, kteri se teprve v aktualnim roce narodi...
23.10.2009 11:17:22
Skutečně jsi to vůbec nepochopil. I kdybys neprohodil podmínku, tak to vrátí jenom lidi, kteří se teprve narodí. Než příště něco napíšeš, tak si to vyzkoušej.
23.10.2009 11:19:53
fos4:
Pokud se nepletu tak u takovych dotazu je vyuziti indexu nulove.
Nebylo by lepsi si ukladat triggerem do pomocneho sloupce za kolik dni budou narozeniny a na nej dat index ?
23.10.2009 09:04:35
Díky za upozornění. Myslel jsem na to, ale zapomněl jsem to do článku napsat. Doplnil jsem to alespoň teď.
23.10.2009 11:23:09
...Dalo by se to udělat tak, že od aktuálního data odečteme datum narození a pokud zbytek po dělení 365 dá nějaké číslo o málo menší než nula,...
Pokud dobre chapu, tak natum narozeni je vzdy mensi nez aktualni datum. Uvedeny rozdil bude tedy vzdy kladny. Jakykoli zbytek po deleni kladneho cisla kladnym (365) nemuze nikdy dat cislo mensi nez nula.
23.10.2009 09:38:47
Jako že číslo kongruentní modulo 365 :-).
Jasně, opravil jsem to.
23.10.2009 11:30:30
Kajman:
Místo where by mělo být having. A když by se místo curdate dalo rovnou '2009-10-23', může se použít sql cache.
Škoda, že mysql stále neumí indexy nad výsledkem funkce, pak by se mohl udělat index jako date_format(narozeni,'%m%d') a tím by už byly rychlé dotazy, třeba něco jako...
select u.*
from uzivatele u,
(select date_format('2009-10-23', '%m%d') d1,
date_format(adddate('2009-10-23', interval 14 day), '%m%d') d2) l
where (date_format(narozeni, '%m%d') >= l.d1 and
date_format(narozeni, '%m%d') <= l.d2)
or ((date_format(narozeni, '%m%d') >= l.d1 or
date_format(narozeni, '%m%d') <= l.d2) and l.d1 > l.d2)
23.10.2009 23:24:56
WHERE jsem opravil. To skoro vypadá, jako kdybych si to ani nevyzkoušel, co? :-)
24.10.2009 05:40:09
Daniel Tlach:
"Tito chudáci v nepřestupný rok narozeniny prostě nemají" - zase takoví chudáci nejsme ;) Vlastně je to lepší, protože stárneme mnohem pomaleji než obyčejná smrtelníci :D
23.10.2009 23:37:09
Nalejou ti aspoň v hospodě? Já jsem se chtěl zeptat, jestli mezi čtenáři někdo takový je, pravděpodobnost mi vyšla 65 % (podle TOPlistu).
24.10.2009 05:48:15
Daniel Tlach:
Nalejvaj a ani občanku nechtěj :)
24.10.2009 19:48:57
papundeklová paní:
Už je to možná off-mísa, ale on v Sovětském svazu v letech 1930 a 1931 existoval i 30. únor
http://cs.wikipedia.org/wiki/30._%C3%BAnor(jóó, v Rusku, tam je možný fšechno :-) )
26.10.2009 22:38:50
V tomto článku je i odkaz na věk a obávám se, že ten bude běžet normálně, takže to až taková výhra IMHO není :P
24.10.2009 09:54:41
Jirka:
to lze snadno resit tak, ze se rok prohodi u data narozeni i u aktualniho data - v obou pripadech na rok, ktery prestupny je. nicmene cele ro reseni mi prijde hrozne krkolomne, ale nemam cas to zkoumat, takze nevylucuju, ze je nejlepsi :)
30.10.2009 21:27:34
kriplozoik:
Mě by jako první řešení tohohle problému (předpokládám, že v MySQL) napadlo porovnávat počet dní v roce datumu narození a aktuálního datumu, tedy DAYOFYEAR(datum_narozeni) a DAYOFYEAR(CURDATE()). Problém s 29. únorem tu je, ale dal by se zvládnout (jak jsem k mému podivu zjistil, tak funkce na zjištění, jestli je přestupný rok, neexistuje ani v MySQL ani v PHP (ne mezi základními funkcemi, ale jakési knihovně MCAL). V MySQL by si šlo samozřejmě pomoci, např. výrazem: DAYOFYEAR(CONCAT(rok,"-12-31"))=366.
26.10.2009 22:57:38
fos4:
v PHP: date("L");
v MYSQL pouze trikem ktery jsi popsal ty..
27.10.2009 09:31:15
Tento článek vznikl jako reakce na pokus vyřešit to právě pomocí DAYOFYEAR… V MySQL by se dalo použít ještě SELECT CAST(CONCAT(rok, '-02-29') AS date) IS NULL.
27.10.2009 09:46:18
Mat:
Dobrý den,
zkoušel jsem tento SQL příkaz v Delphi5, ale hlásí mi to hybu "invalid use of keyword YEAR(CURDATE()) " nevíte v čem by mohl být problém?
příkaz:
SELECT DATEDIFF(CONCAT(YEAR(CURDATE()) + IF(SUBSTR(narozeniny, 5) < SUBSTR(CURDATE(), 5), 1, 0), SUBSTR(narozeni, 5)), CURDATE()) AS narozeniny_za FROM "'+databaze+'" HAVING narozeniny_za BETWEEN 0 AND 14
Děkuji za odpověĎ
16.3.2011 22:27:06
Dotaz je určen pro MySQL, v jiných databázích nemusí být tato funkce k dispozici.
16.3.2011 22:28:25