Wszystkie zagadnienia: Bazy danych
zaawansowany SQLdatyDateDiffmatura rozszerzona z informatyki

Funkcje daty

Praca z datami w SQL – data bieżąca, różnica dat w latach i dniach, wyciąganie roku i miesiąca, BETWEEN na datach oraz zapis daty w warunku.

01

Opis

Data w bazie nie jest tekstem. Wygląda jak tekst na ekranie, ale przechowywana jest jako liczba – co ma jedną bardzo praktyczną konsekwencję: daty można od siebie odejmować, a wynikiem jest liczba dni między nimi. Na tej jednej właściwości opiera się większość zadań z datami.

Z tego samego powodu daty da się porównywać zwykłymi operatorami: data późniejsza jest „większa” od wcześniejszej. Warunek „wizyty po 16 maja” to po prostu > z odpowiednią datą po prawej stronie.

Zapis daty w warunku to najczęstsze źródło kłopotów. Nie wystarczy napisać jej w apostrofach jak tekst – trzeba powiedzieć bazie, że to data. Access wymaga otoczenia jej znakami # i zapisu w formacie amerykańskim (miesiąc, dzień, rok), niezależnie od ustawień systemu. Alternatywnie można użyć funkcji, która zamienia datę zapisaną po polsku na wewnętrzny format bazy – to zapis bezpieczniejszy, bo nie wymaga pamiętania o kolejności.

Data bieżąca jest dostępna jako funkcja, którą wywołuje się bez argumentów. Warto rozróżnić funkcję zwracającą samą datę od tej, która zwraca datę razem z godziną – przy odejmowaniu różnica potrafi zmienić wynik o jeden dzień.

Różnica dat w wybranej jednostce. Odejmowanie daje wynik w dniach. Gdy potrzebujesz lat, miesięcy czy kwartałów, służy do tego osobna funkcja, której pierwszym parametrem jest jednostka. Uwaga: liczy ona przekroczone granice jednostki, a nie pełne okresy – różnica lat między 31 grudnia a 1 stycznia wyniesie 1, mimo że minął jeden dzień.

Stąd bierze się klasyczna pułapka z liczeniem wieku. Odjęcie samych roczników daje wynik zawyżony dla wszystkich, którzy w tym roku jeszcze nie mieli urodzin. Dokładniejszy sposób to policzyć różnicę w dniach i podzielić przez średnią długość roku, a wynik obciąć do liczby całkowitej.

Wyciąganie części daty. Osobne funkcje zwracają z daty sam rok, sam miesiąc albo sam dzień. To wygodniejsze niż wycinanie fragmentów tekstu i działa niezależnie od formatu wyświetlania. Po tak wyciągniętej wartości można też grupować – na przykład zliczyć zdarzenia w poszczególnych latach.

  • Data to liczba, nie tekst – dlatego można ją odejmować i porównywać zwykłymi operatorami >, <.
  • Format zapisu zależy od silnika – Access chce znaków # i kolejności miesiąc/dzień/rok, standard SQL – apostrofów i kolejności rok-miesiąc-dzień.
  • Funkcje różnicy dat liczą przekroczone granice jednostki, a nie pełne okresy – stąd biorą się pułapki przy liczeniu wieku.
  • Dokładny wiek liczy się przez dni, nie przez odjęcie roczników – różnica w dniach podzielona przez 365,25 i obcięta do liczby całkowitej.
02

Funkcje

Date()
dzisiejsza dataBez godziny. Wywoływana bez argumentów.
Now()
data i godzinaJak wyżej, ale z czasem – uwaga przy odejmowaniu.
Year(data)
rok z datyZwraca liczbę czterocyfrową.
Month(data)
miesiąc z datyLiczba od 1 do 12.
Day(data)
dzień miesiącaLiczba od 1 do 31.
Weekday(data)
dzień tygodniaLiczba od 1 do 7. W Accessie domyślnie 1 to niedziela, 7 to sobota.
DateDiff("jednostka"; od; do)
różnica w zadanej jednostceJednostka jako tekst: "yyyy" rok, "m" miesiąc, "d" dzień, "q" kwartał.
DatePart("jednostka"; data)
część datyUniwersalna alternatywa dla Year i Month, przydatna dla kwartałów.
03

Dialekt

W AccessieStandardowy SQLZnaczenie
Date() CURRENT_DATE dzisiejsza data
#5/16/2024# DATE '2024-05-16' data wpisana wprost w warunku
DateDiff("d"; a; b) (b - a) różnica w dniach
Year(data) EXTRACT(YEAR FROM data) rok z daty
04

Zapytanie

-- Data wpisana wprost: w Accessie w znakach # i w formacie amerykańskim
SELECT nazwisko, data_ur
FROM uczniowie
WHERE data_ur > #1/1/2007#
Wynik zapytania
nazwiskodata_ur
Kowalska2007-03-12
Wiśniewski2007-01-28
Dąbrowski2007-08-30
-- Rok wyciągnięty funkcją Year() użyty jako warunek w WHERE
SELECT nazwisko, data_ur
FROM uczniowie
WHERE Year(data_ur) = 2007
ORDER BY nazwisko
Wynik zapytania
nazwiskodata_ur
Dąbrowski2007-08-30
Kowalska2007-03-12
Wiśniewski2007-01-28
-- Uczniowie urodzeni w pierwszym półroczu roku, wg miesiąca urodzenia
SELECT nazwisko, Month(data_ur) AS miesiac
FROM uczniowie
WHERE Month(data_ur) <= 5
ORDER BY miesiac
Wynik zapytania
nazwiskomiesiac
Wiśniewski1
Kowalska3
Zając5
-- Grupowanie po roku wyciągniętym z daty
SELECT Year(data_ur) AS rocznik, COUNT(*) AS ilu
FROM uczniowie
GROUP BY Year(data_ur)
Wynik zapytania
rocznikilu
20062
20073
-- BETWEEN na datach - obie granice włącznie, obie w znakach #
SELECT nazwisko, data_ur
FROM uczniowie
WHERE data_ur BETWEEN #1/1/2007# AND #12/31/2007#
ORDER BY data_ur
Wynik zapytania
nazwiskodata_ur
Wiśniewski2007-01-28
Kowalska2007-03-12
Dąbrowski2007-08-30
-- Wiek liczony w dniach i dzielony przez długość roku - dokładniejszy niż
-- różnica roczników. Dzień odniesienia w tym przykładzie: 7 maja 2026.
SELECT nazwisko, Int(DateDiff("d"; data_ur; #5/7/2026#) / 365.25) AS wiek
FROM uczniowie
ORDER BY wiek DESC, data_ur ASC
Wynik zapytania
nazwiskowiek
Zając19
Nowak19
Wiśniewski19
Kowalska19
Dąbrowski18
-- Pulapka: data w apostrofach jak tekst, nie w znakach #
SELECT nazwisko
FROM uczniowie
WHERE data_ur = '2007-03-12'
Wynik zapytania
efekt
błąd niezgodności typów danych w wyrażeniu kryterium (zależnie od instalacji: po prostu pusty wynik)
05

Typowe błędy

Data w apostrofach zamiast w znakach #. Zła wersja: WHERE data_ur = '2007-03-12'. Baza widzi to jako porównanie z tekstem, nie z datą, i zwykle zgłasza niezgodność typów danych albo w ogóle nie znajduje wiersza. Poprawka to znaki # i format amerykański: WHERE data_ur = #3/12/2007#.

Dzień i miesiąc zamienione miejscami. #16/5/2024# wygląda naturalnie po polsku (16 maja), ale Access czyta pierwszą liczbę jako miesiąc – miesiąca 16 nie ma, więc zgłasza błąd. Poprawny zapis to #5/16/2024#: najpierw miesiąc, potem dzień.

Wiek liczony odjęciem samych roczników. Year(Date()) − Year(data_ur) zawyża wiek każdemu, kto w tym roku jeszcze nie miał urodzin – w naszej tabeli dotyczy to trzech z pięciu uczniów. Dokładniejszy sposób liczy różnicę w dniach i dzieli przez 365,25.

DateDiff z jednostką roku brany za pełne lata. DateDiff("yyyy"; data_ur; Date()) liczy, ile razy przekroczono granicę 31 grudnia/1 stycznia, a nie ile pełnych lat minęło – to ta sama pułapka co wyżej, tylko ukryta za nazwą funkcji, która brzmi precyzyjnie.

Porównanie z Now() zamiast Date() przy kolumnie bez godziny. WHERE data_zdarzenia = Now() prawie nigdy nie znajdzie dopasowania, bo Now() zwraca też godzinę i sekundy, a data_zdarzenia ma same zera po północy. Do porównań „ten sam dzień” służy Date(), do zapisania, kiedy dokładnie coś się stało – Now().

Zapomniany drugi znak # w BETWEEN. WHERE data_ur BETWEEN #1/1/2007# AND 12/31/2007 bez znaków wokół drugiej daty to błąd składni albo, gorzej, wyrażenie policzone jak dzielenie liczb. Każda z dwóch granic w Accessie potrzebuje własnej pary #...#.

06

Zapamiętaj

  • Data w bazie to liczba – dlatego da się ją odejmować i porównywać operatorami >, <.
  • Access wymaga znaków # i kolejności miesiąc/dzień/rok; standard SQL – apostrofów i kolejności rok-miesiąc-dzień.
  • Year(), Month(), Day() wyciągają pojedynczą część daty i działają niezależnie od formatu wyświetlania.
  • DateDiff liczy przekroczone granice jednostki, nie pełne okresy – stąd pułapka przy liczeniu wieku.
  • Dokładny wiek to różnica w dniach podzielona przez 365,25 i obcięta do liczby całkowitej.
  • BETWEEN na datach obejmuje obie granice włącznie – każdą z nich trzeba osobno otoczyć znakami #.
  • Date() zwraca samą datę, Now() datę z godziną – przy porównaniach ta różnica bywa źródłem błędu.
07

Sprawdź się

Fragment tabeli uczniowie – tylko kolumny potrzebne do dat:

nazwiskodata_ur
Kowalska2007-03-12
Nowak2006-11-05
Wiśniewski2007-01-28
Zając2006-05-19
Dąbrowski2007-08-30

Uzupełnij odpowiedzi na podstawie tabeli:

PytanieOdpowiedź
Ile wierszy zwróci SELECT nazwisko FROM uczniowie WHERE Year(data_ur) = 2007?
Jaka funkcja wyciąga z daty sam miesiąc?
Jaki znak otacza literał daty w zapytaniach Accessa?
Który uczeń ma najmniejszą wartość Month(data_ur)?
Ile wynosi Year(data_ur) dla Zającego?
Który operator sprawdza obie granice przedziału dat włącznie?
Która funkcja zwraca datę razem z godziną?
Ile dni pokaże DateDiff dla jednostki dni między 1 stycznia a 31 grudnia tego samego roku (nieprzestępnego)?

Warunek na przedziale WHERE data_ur BETWEEN #1/1/2007# AND #12/31/2007# obejmuje obie granice włącznie, dlatego znalazłby też ucznia urodzonego 2007 roku. Data porównana w apostrofach zamiast w znakach # bywa odczytana jako zwykły , więc warunek może nie znaleźć żadnego wiersza.

08

Najczęściej zadawane pytania

Dlaczego różnica dat daje dziwnie dużą liczbę?

Bo wynik jest w dniach. 6500 to nie błąd, tylko mniej więcej osiemnaście lat. Jeśli potrzebujesz innej jednostki, użyj funkcji liczącej różnicę z podaną jednostką zamiast zwykłego odejmowania.

Czy da się sortować po dacie?

Tak, i to poprawnie chronologicznie – bo data jest liczbą, a nie tekstem. To jedna z przewag trzymania dat w kolumnie typu data zamiast w tekście: tekst '01.12.2024' posortowałby się przed '02.01.2024'.

Jak wybrać wszystkie zdarzenia z jednego miesiąca?

Najczytelniej przez warunek na przedziale dat – od pierwszego dnia miesiąca do ostatniego – albo przez porównanie wyciągniętego roku i miesiąca. Drugie podejście bywa wolniejsze na dużych tabelach, ale na maturze jest zupełnie wystarczające.

Czym różni się Year() od DatePart()?

Year() zawsze zwraca rok i tylko rok. DatePart() jest uniwersalna – tym samym poleceniem, zmieniając wyłącznie napis jednostki ("m", "d", "q"), można wyciągnąć dowolną część daty, więc bywa wygodniejsza, gdy jednostka zależy od parametru zapytania. Do samego roku czytelniej jest jednak użyć Year().

Jak policzyć dokładny wiek w latach, a nie w przybliżeniu?

Najdokładniej przez porównanie dnia i miesiąca urodzenia z dzisiejszą datą w wyrażeniu warunkowym, a nie samym dzieleniem różnicy dni przez 365,25 – to przybliżenie, które w pojedynczych przypadkach potrafi się pomylić o rok w okolicy urodzin. Na maturze wystarcza zwykle przybliżenie przez dni.

Czy BETWEEN na datach działa tak samo jak na liczbach?

Tak – obie granice są włączone, dokładnie tak samo jak dla liczb i tekstu. Różnica jest tylko w zapisie granic: w Accessie każda z dat musi być otoczona znakami #, a nie apostrofami.

Co zwróci Year(Null)?

Wartość Null. Funkcje daty, podobnie jak większość funkcji w SQL, przenoszą brak wartości dalej – jeśli data urodzenia nie jest wypełniona, żadna funkcja wyciągająca jej część też nie da wyniku.

Przećwicz w zadaniach maturalnych

Do tego hasła nie mamy jeszcze przypisanych pojedynczych zadań. Cały spis zadań maturalnych z tego działu jest tutaj:

Zadania z działu: SQL

↗

Powiązane zagadnienia