Buduję bazę danych w Postgres, gdzie będzie dużo grupowania rzeczy według month
i year
, ale nigdy przez date
.
- Mógłbym utworzyć liczby całkowite
month
iyear
kolumny i użyć ich. - Lub mógłbym mieć
month_year
kolumnę i zawsze ustawić naday
1.
Ten pierwszy wydaje się nieco prostszy i jaśniejszy, jeśli ktoś patrzy na dane, ale drugi jest fajny, ponieważ używa odpowiedniego typu.
postgresql
database-design
datetime
David N. Welton
źródło
źródło
month
który zawiera dwie liczby całkowite. Ale myślę, że jeśli nigdy, nigdy nie potrzebujesz dnia miesiąca, użycie dwóch liczb całkowitych jest prawdopodobnie łatwiejszeOdpowiedzi:
Osobiście, jeśli jest to data lub może to być data, sugeruję, aby zawsze przechowywać ją jako jedną. Zasadniczo łatwiej jest pracować.
Możesz mieć jedną datę, która będzie wspierać dzień, jeśli kiedykolwiek będziesz jej potrzebować, lub jedną
smallint
na rok i miesiąc, która nigdy nie zapewni dodatkowej precyzji.Przykładowe dane
Spójrzmy teraz na przykład. Utwórzmy milion dat dla naszej próbki. To około 5000 wierszy na 200 lat między 1901 a 2100. Każdego roku powinno być coś na każdy miesiąc.
Testowanie
Prosty
WHERE
Teraz możemy przetestować teorie nieużywania daty. Każdą z nich przeprowadziłem kilka razy, aby rozgrzać.
Teraz wypróbujmy inną metodę z osobnymi
Szczerze mówiąc, nie wszystkie są 0,749 .. niektóre są trochę mniej więcej, ale to nie ma znaczenia. Wszystkie są względnie takie same. To po prostu nie jest potrzebne.
W przeciągu jednego miesiąca
Teraz bawmy się dobrze. Załóżmy, że chcesz znaleźć wszystkie interwały w ciągu 1 miesiąca od stycznia 2014 r. (Tego samego miesiąca, którego użyliśmy powyżej).
Porównaj to z metodą łączoną
Jest zarówno wolniejszy, jak i brzydszy.
GROUP BY
/ORDER BY
Metoda łączona,
I znowu metodą kompozytową
Wniosek
Zasadniczo niech mądrzy ludzie wykonują ciężką pracę. Datemath jest trudny, moi klienci nie płacą mi wystarczająco. Robiłem te testy. Trudno mi było dojść do wniosku, że mogę uzyskać lepsze wyniki niż
date
. Przestałem próbować.AKTUALIZACJE
@ Koń_nazwa_na_nazwy sugerowany do mojego testu w ciągu jednego miesiąca
WHERE (year, month) between (2013, 12) and (2014,2)
. Moim zdaniem, choć fajne, jest to bardziej złożone zapytanie i wolałbym go unikać, chyba że byłby to zysk. Niestety, było jeszcze wolniej, chociaż jest blisko - co jest bardziej oddalone od tego testu. To po prostu nie ma większego znaczenia.źródło
date
w większości przypadków jest to droga.Jako alternatywę dla zaproponowanej przez Evana Carrolla metody, którą uważam za prawdopodobnie najlepszą opcję, w niektórych przypadkach (i nie specjalnie przy użyciu PostgreSQL) korzystałem tylko z
year_month
kolumny typuINTEGER
(4 bajty), obliczonej jakoOznacza to, że kodujesz miesiąc na dwóch najbardziej po prawej stronie cyfr dziesiętnych (cyfra 0 i cyfra 1) liczby całkowitej, a rok na cyfrach od 2 do 5 (lub więcej, jeśli to konieczne).
Jest to do pewnego stopnia alternatywa biednego człowieka do budowania własnego
year_month
typu i operatorów. Ma pewne zalety, głównie „jasność intencji” i pewne oszczędności miejsca (chyba nie w PostgreSQL), a także pewne niedogodności związane z posiadaniem dwóch osobnych kolumn.Możesz zagwarantować, że wartości są prawidłowe, po prostu dodając
Możesz mieć
WHERE
klauzulę wyglądającą jak:i działa skutecznie (
year_month
oczywiście jeśli kolumna jest właściwie zindeksowana).Możesz grupować w
year_month
ten sam sposób, w jaki robisz to z datą i z tą samą wydajnością (przynajmniej).Jeśli potrzebujesz oddzielić
year
imonth
, obliczenia są proste:Co jest niewygodne : jeśli chcesz dodać 15 miesięcy
year_month
, musisz obliczyć (jeśli nie popełniłem błędu lub przeoczenia):Jeśli nie będziesz ostrożny, może to być podatne na błędy.
Jeśli chcesz uzyskać liczbę miesięcy między dwoma miesiącami rocznymi, musisz wykonać podobne obliczenia. To (z wieloma uproszczeniami) to, co naprawdę dzieje się pod maską z arytmetyką dat, która na szczęście jest przed nami ukryta przez już zdefiniowane funkcje i operatory.
Jeśli potrzebujesz wielu z tych operacji, używanie
year_month
nie jest zbyt praktyczne. Jeśli tego nie zrobisz, jest to bardzo jasny sposób na wyjaśnienie swoich zamiarów.Alternatywnie możesz zdefiniować
year_month
typ i zdefiniować operatoryear_month
+interval
, a także innyyear_month
-year_month
... i ukryć obliczenia. Nigdy tak naprawdę nie wykorzystałem tak często, aby poczuć potrzebę w praktyce. Adate
- wdate
rzeczywistości ukrywa cię coś podobnego.źródło
Jako alternatywa dla metody joanolo =) (przepraszam, byłem zajęty, ale chciałem to napisać)
BIT RADOŚĆ
Zrobimy to samo, ale z kawałkami. Jedna
int4
w PostgreSQL jest liczbą całkowitą ze znakiem, od -2147483648 do +2147483647Oto przegląd naszej struktury.
Przechowywanie miesiąca.
pow(2,4)
to 4 bity .Oto nasza mapa bitowa miejsc, w których przechowywane są miesiące.
Miesiące, 1 stycznia - 12 grudnia
Lat Pozostałe 28 bitów pozwala nam przechowywać nasze informacje dotyczące roku
W tym momencie musimy zdecydować, w jaki sposób chcemy to zrobić. Do naszych celów moglibyśmy użyć przesunięcia statycznego, gdybyśmy tylko musieli pokryć 5000 AD, moglibyśmy wrócić do
268,430,455 BC
tego, do którego w zasadzie pokrywa się całość mezozoiku i wszystko, co przydatne, by iść naprzód.A teraz mamy podstawy naszego typu, które wygasną za 2700 lat.
Przejdźmy więc do tworzenia niektórych funkcji.
Szybki test pokazuje, że to działa ..
Teraz mamy funkcje, których możemy używać na naszych typach binarnych.
Moglibyśmy odciąć jeszcze jeden bit od podpisanej części, zapisać rok jako dodatni, a następnie posortować go naturalnie jako podpisaną liczbę całkowitą. Gdyby prędkość miała wyższy priorytet niż miejsce do przechowywania, byłaby to droga, którą zejdziemy. Ale na razie mamy datę, która działa z mezozoikiem.
Mogę później to zaktualizować, dla zabawy.
źródło