Wyszukiwanie najbliższej daty w Django / SQL
26 maja, 2009
Zdarzyła mi się ostatnio potrzeba wyszukania w ORM Django obiektów o czasie utworzenia najbliższemu wybranej dacie. W czeluściach internetu nie znalazłem zbyt wiele sensownego na temat szukania najbliższych wartości, nie było też żadnej automagicznej funkcji wykonującej podobne zadanie, więc zdecydowałem się napisać do tego własny kawałek kodu SQL.
Jedynym sensownym rozwiązaniem, na jakie wpadłem jest użycie DATEDIFF jako "wyznacznika odległości" od poszukiwanej daty.
I tak, wspomniana funkcja pozwala nam w prosty sposób obliczyć różnicę dni pomiędzy wybranymi obiektami typu DATE lub DATETIME:
SELECT DATEDIFF('2009-05-01','2009-04-12'); 12
Praktyczny przykład z bazy danych:
SELECT companies_ownership.start_at, DATEDIFF(companies_ownership.start_at,'2009-04-3') as diff FROM companies_ownership; +------------+------+ | start_at | diff | +------------+------+ | 2009-01-01 | -92 | | 2009-03-04 | -30 | | 2009-03-04 | -30 | | 2009-04-04 | 1 | | 2009-04-04 | 1 | | 2009-05-10 | 37 | +------------+------+ 6 rows in set (0.00 sec)
Jeśli potrzebujemy znaleźć np. najbliższy lub większy element, dodajemy odpowiedni warunek do zapytania:
SELECT companies_ownership.start_at, DATEDIFF(companies_ownership.start_at,'2009-04-3') as diff FROM companies_ownership WHERE companies_ownership.start_at >= DATE('2009-04-3'); +------------+------+ | start_at | diff | +------------+------+ | 2009-04-04 | 1 | | 2009-04-04 | 1 | | 2009-05-10 | 37 | +------------+------+ 3 rows in set (0.00 sec)
Wystarczy teraz dodać LIMIT i mamy najbliższy lub większy element. Analogicznie postępujemy z najbliższym lub mniejszym - dodajemy warunek mniejszości i dodatkowo zmieniamy kolejność sortowania ORDER BY.
Jeśli jednak potrzeba nam elementu najbliższego bez względu na jego położenie, należy wyznaczyć wartość absolutną z wyniku DATEDIFF, a wynik najbliższy zeru będzie naszą szukaną wartością:
SELECT companies_ownership.start_at, id, ABS(DATEDIFF(start_at,'2009-04-3')) as diff FROM companies_ownership ORDER BY diff ASC LIMIT 1 +------------+------+ | start_at | diff | +------------+------+ | 2009-04-04 | 1 | +------------+------+ 1 row in set (0.00 sec)
Integracja z Django
Osobiście kod potrzebuję do filtrowania obiektów Ownership. Dodaję go zatem do Managera obiektu, korzystając z funkcji extra(), która posiada możliwość wstawiania własnych fragmentów kodu do zapytania ORM.
class OwnershipManager(models.Manager): def nearest_change(self, owner, date): # wstepne przefiltrowanie wynikow wg wybranego obiektu Owner qs = self.get_query_set().filter(owner = owner,) # wyszukiwanie najblizszej wartosci return qs.extra(select={'diff': "SELECT ABS(DATEDIFF( start_at, '%s'))"% date }).order_by('diff')[0:1]
Wpis dla potomnych. Może się przyda :)
Komentarze do wpisu "Wyszukiwanie najbliższej daty w Django / SQL":
1.
occulkot napisał(a):
26 maja 2009, 13:01:23
DateDiff?? coz to za funkcja? M$SQL?
2.
chester napisał(a):
26 maja 2009, 13:03:55
MySQL, widziałem też (implementacje?) do Postgresa
3.
occulkot napisał(a):
26 maja 2009, 13:09:11
@chester: jak sprawdzilem w postgresie zalecaja uzycia zwyklego odejmowania ;). MySQL jak zwykle zaskakuje wlasnymi funkcjami.
Aczkolwiek sam pomysl z pobraniem najblizszych elementow dla daty calkiem ciekawy. Nie widzialem czego takiego nigdzie ;)
4.
chester napisał(a):
26 maja 2009, 13:15:39
Hm.. ja ‘nagle’ zapotrzebowałem czegoś takiego, więc jak tylko znalazłem datediff to tylko sprawdziłem czy w innych bazach piszą o tym. A że znalazłem pod hasłami „datediff postresql” to pomyślałem, że to pewnie standard i wziąłem się za pisanie reszty nie szukając dalej ;)
Inna sprawa – używam języka SQL na tyle ile muszę (ORM. Wiadomo jak jest ;) ) i jakoś nie wpadłem na samo odejmowanie ;)) Sprawdzę w wolnej chwili.
5.
occulkot napisał(a):
26 maja 2009, 13:16:46
@chester: http://archives.postgresql.org/pgsql-sql/2001-06/msg00556.php – zdaniem panow z postgresql ;)
6.
chester napisał(a):
26 maja 2009, 13:19:05
niech będzie ;) Potestuję i wrzucę poprawkę.
Dodaj komentarz: