Dlaczego nie zwrócić dat w postaci ciągu z bazy danych?

41

W typowej aplikacji internetowej daty są pobierane z warstwy bazy danych silnie wpisanej (np. W c # jako System.DateTime w przeciwieństwie do System.String).

Gdy data musi być wyrażona jako ciąg (np. Wyświetlany na stronie), konwersja z DateTime na ciąg odbywa się w warstwie prezentacji.

Dlaczego to? Dlaczego konwersja DateTime na ciąg znaków w warstwie bazy danych jest złą rzeczą?

Zobacz także gorącą debatę na czacie i oryginalne pytanie, które to wszystko rozpoczęło .

John Wu
źródło
73
Pozwól, że zapytam: czy po prostu przekształciłbyś każdy typ w ciąg? Co wyróżnia Data?
ogrodnik
7
Dobre pytanie! Tutaj możesz zobaczyć ożywioną debatę .
John Wu,
8
Cóż, wydaje się oczywiste, że ten drugi facet się myli, a wszyscy inni mają rację. Naprawdę nie ma tu pytania
ogrodnik
7
Czasami musisz wykonać matematykę daty poza bazą danych. Znacznie trudniej, jeśli masz tylko sznurki.
Eric King,
14
Kolejny problem - jakiego rodzaju sznurka potrzebujesz? Istnieje wiele sposobów reprezentowania daty i godziny jako ciągu. Co jeśli miałbym bazę danych, która zwróciła tylko bieżący czas, reprezentowany jako # sekund od epoki, jako ciąg (na przykład bieżący czas to „1474496980”). Czy byłoby to przydatne? Czy chcesz użyć takiej bazy danych?
riwalk

Odpowiedzi:

168

Daty, DateTimes i naprawdę każdy inny wpisany obiekt powinien być generalnie pozostawiony w swoim poprawnie wpisanym formacie do momentu, kiedy będzie trzeba go przekształcić w inny typ - szczególnie, gdy ten typ jest czytelny dla człowieka, a zwłaszcza gdy jest stratny / konwersja jednokierunkowa.

Dlaczego? Ponieważ zakłada się, że ten typ zapewnia wiele przydatnych wbudowanych funkcji, takich jak prawidłowe testowanie równości, dodawanie i odejmowanie, porównywanie (większe niż, mniejsze niż), strefa czasowa i funkcjonalność ustawień regionalnych (szczególnie ważne dla wszystkiego, co dotyczy czasu), itd. Jeśli zdecydujesz, że chcesz wspierać Amerykanów i format „Dzień miesiąca [th], rok”, a także wspólny brytyjski styl „Dzień Miesiąc Rok” lub standard ISO „Rok-miesiąc-dzień”? Co byś zrobił, gdyby był to ciąg znaków i trzeba wprowadzić tę zmianę, przekształcić go z powrotem w datę? Nie, dziękuję - w ten sposób jest wiele złych i nikczemnych błędów, których najlepiej całkowicie uniknąć.

Mówiąc dokładniej, wspomniałeś o architekturze warstwowej, która później ma warstwę prezentacji oddzielną od danych. Jest to w rzeczywistości kolejny duży powód, aby data podawać jako datę, a nie ciąg - ponieważ w jakim typie formatowania ciągu należy wprowadzić datę? Angielski, chiński, z sekundami / milisekundami lub bez, pełna nazwa miesiąca lub cyfry, czy chcesz później posortować według pola daty (sortowanie według ciągu wymaga określonego formatu ciągu, jeśli chcesz, aby działało poprawnie) itp.? To wszystko kwestia prezentacji - w jaki sposób użytkownik powinien przeglądać dane - a umieszczenie tej logiki gdziekolwiek indziej ograniczyłoby przewagę posiadania architektury warstwowej. Baza danych nie powinna wiedzieć ani dbać o to, jak chcesz zobaczyć datę w przyszłości.

Wreszcie, prawie wszystkie złożone aplikacje (po to są wielopoziomowe architektury), które dbają o czas, nieuchronnie wykorzystują czasy / daty na wiele, wiele różnych sposobów, a często na wszystkich poziomach architektury. Wpisane obiekty związane z czasami i datami istnieją z naprawdę dobrego powodu: sam czas, a zwłaszcza ludzkie systemy kalendarza, są dziwne i trudne. Ostatecznie czasy i daty nie są łańcuchami z tego samego powodu, że liczby całkowite i zmiennoprzecinkowe nie są łańcuchami, a utrudni ci życie tylko wtedy, gdy spróbujesz udawać, że są to po prostu tablice postaci, ponieważ po prostu nie są.

BrianH
źródło
26
+1 tylko za użycie tego złośliwego słowa. Zgadzam się z twoimi przekonującymi argumentami i wyczerpującym wyjaśnieniem, ale dlatego musiałem się zalogować i zagłosować na ciebie.
Adrian Larson
1
Reprezentowanie daty i godziny w sekundach od określonego czasu w przeszłości jest również niezawodne wśród różnych kalendarzy. Np. Kalendarze islamskie i chińskie nie wykorzystują żadnego gregoriańskiego miesiąca, roku itp. Uważam, że obchodzenie się z tym na poziomie bazy danych jest złą praktyką.
rexkogitans
Daty są często przedstawiane jako „X dni temu”. Powodzenia parsowania tego z powrotem do pierwotnej wartości.
Agent_L,
5
Nie zapominajmy również o zmianach DST (i innych podobnych) problemach. Czy „6 listopada 2016 01:30:26” będzie po raz pierwszy, czy drugi raz, kiedy ta data i godzina się wydarzy? UTC DateTime jest co najmniej unikatowy i zawsze możesz przełożyć go na lokalną reprezentację na ten czas - cofnięcie się w drugą stronę nie zawsze jest możliwe.
J ...
3
Why? Because it is assumed that the type provides you with lots of handy built in functionalityMoim zdaniem jest to tylko kwestia drugorzędna. Prawdziwym powodem jest to, że typ mówi ci, czymś jest . Data nie jest ciągiem, po prostu łatwo przekłada się na ciąg czytelny dla człowieka.
Doval
53

Mówi, aby użyć serwera WWW do konwersji czasu danych na ciąg znaków. Mówię, zrób to na serwerze bazy danych, a nie na serwerze WWW. Jak myślisz, dlaczego tak jest lepiej? - Szef MT

Chcę znać typ.

Naprawdę nie dbam o to, czy twoja baza danych przechowuje informacje w postaci ciągu, niektórych liczb całkowitych lub bajtów, ponieważ na końcu i tak zawsze są bajty. Ten ciąg zajmujący więcej miejsca niż potrzeba w bazie danych nie przeszkadza mi. To, co mnie martwi, to takie randki:

11/10/2016

I nie wiedząc, czy to jedenasty, czy dziesiąty miesiąc.

Ale, jak mówisz, jest to potwierdzone. Pewnie poddasz go procesom sprawdzania poprawności. Data jest całkowicie poprawna. Ale tutaj utrzymuję tę rzecz i wszystko, co wiem, to data to ciąg znaków. Nie mogę nawet powiedzieć, jaka to data.

„Dziesiąty dzień listopada dwa tysiące szesnastego roku naszego pana”.

To jest struna. Jedna z naszych prezentacji potrzebuje go w tym formacie. Powiedziałeś, że baza danych konwertuje wszystkie daty na ciągi, prawda? Baw się dobrze z tym.

Zadaniem bazy danych jest przechowywanie danych nieobecnych danych. Jasne, możesz to zrobić w ciągach, ale musisz to przeanalizować, aby było przydatne do prezentacji w innych formatach. Przechowywanie go w standardowej parsowanej formie dla dowolnego typu oferowanego przez DB pozwala nam być tak blisko gotowości do prezentacji, jak to tylko możliwe, bez podjęcia decyzji o prezentacji. Naprawdę nie ma dla mnie znaczenia, czy DB popiera ten typ ciągiem znaków, liczbami całkowitymi lub bajtami. Tak długo, jak wie, co robi.

Ale kiedy nie poinformujesz DB, że mamy do czynienia z datą i przechowujemy datę jako ciąg znaków, przedwcześnie prezentujesz i preferujesz jedną prezentację nad wszystkimi innymi. To zmusza wszystkich innych prezenterów do parsowania przed konwersją. Nie, baza danych nie jest częścią warstwy prezentacji. Nie proś o to.

Podobnie warstwa prezentacji nie jest częścią bazy danych, więc nie jest rozsądne łączenie raportu ze szczegółami bazy danych. Znacznie bardziej niezawodne jest działanie na typach.

candied_orange
źródło
Ta odpowiedź dotyczy przechowywania jako ciągów. Jednak nie odnosi się do wspólnego wzorca przechowywania dat w rodzimym typie daty, ale formatuje go do ciągu znaków w zapytaniu SQL , używając funkcji takich jak CONVERT (T-SQL), ani też, że DBMS zazwyczaj serializuje swoje daty na ciąg znaków w konfigurowalnym formacie niezależnie od zapytania. Na przykład: postgresql.org/docs/9.5/static/…
dcorking
To jest raport. Zdarza się to po przechowywaniu. Jak konwersja mojej daty urodzenia na mój wiek.
candied_orange
2
Chciałem tylko zachęcić cię do rozszerzenia odpowiedzi, ponieważ tematem PO jest to, w jaki sposób „daty są pobierane z warstwy bazy danych”. Istnieje dobrze ustalony, choć prawdopodobnie przestarzały, wzorzec, w którym raport wysyła zapytanie do bazy danych o sformatowane i zlokalizowane ciągi dat. Myślę, że OP chciałby usłyszeć te argumenty dotyczące rezygnacji. Wiem, że bym to zrobił.
dcorking
@ aktualizacja notatki korygującej.
candied_orange
+1 dodając więcej wody do młyna: po prostu stwórz system na zainstalowanej podstawie, który obejmuje wiele stref czasowych, w których absolutna chwila jest najważniejsza, i zobacz, jak sobie radzisz z konwersjami znaczników czasu <-> wszędzie. Co najgorsze, zrób punkt rozszerzenia, aby ludzie mogli tworzyć własne wtyczki i nadawać im znaczniki czasu jako ciągi znaków, aby zobaczyć, jak spójne będą te znaczniki czasu!
Newtopian
19

Widownia

Konwersja daty na ciąg znaków w celu prezentacji wymaga znajomości preferencji użytkownika, ponieważ dokładnie ta sama data powinna być wyświetlana inaczej dla użytkowników w różnych lokalizacjach. Nawet jeśli używasz jednego ustawienia narodowego w aplikacji, właściwe zachowanie powinno używać ustawień regionalnych aplikacji zamiast serwera bazy danych; i nie gwarantuje się, że będą identyczne, nawet jeśli w tym momencie przypadkowo się zgadzają.

Konwersja z uniwersalnego typu danych daty na ciąg znaków specyficznych dla ustawień regionalnych powinna mieć miejsce w warstwie prezentacji, ponieważ jest to warstwa, która wie, w jaki sposób należy wykonać tę konwersję.

Piotr jest
źródło
3
Przykładem niedopasowania regionalnego może być pisanie aplikacji dla użytkowników Maine w USA, a następnie hostowanie jej w farmie serwerów zachodniego wybrzeża Amazonii. ;) W rzeczywistości nie jest to wcale taka mało prawdopodobna sytuacja.
jpmc26
@ jpmc26 Nie rozumiem różnicy - czy Maine używa innego formatu daty niż reszta USA?
Pete Kirkham,
2
@PeteKirkham Maine i zachodnie wybrzeże USA używają stref czasowych, które są w odległości 3 godzin.
jpmc26,
1
Lub inny scenariusz z życia: wyobraź sobie uruchomienie serwera w Szwajcarii, który musi obsługiwać klientów w czterech (niemieckim, francuskim, włoskim, angielskim) różnych językach z różnymi lokalizacjami (i nieco innymi regułami formatowania). Powodzenia w wyborze odpowiedniej lokalizacji dla swojego serwera w takiej sytuacji.
Voo
1
Strefy czasowe i ustawienia regionalne @ jpmc26 to nie to samo. Na przykład mamy biura w Glasgow w Szkocji, Atlancie w USA i Pune w Indiach. Konsultanci w tych biurach z kolei monitorują witryny (kampusy, szpitale, hotele itp.) Na całym świecie przez całą dobę. Baza danych aplikacji działa w UTC, ale wyświetla czasy w czasie lokalnym dla monitorowanej witryny. Konsultanci w USA mają daty zlokalizowane w MM / DD / RRRR, ale lokalizacje w Wielkiej Brytanii i Indiach to DD / MM / RRRR - zależy to od lokalizacji, a nie strefy czasowej witryny lub użytkownika.
Pete Kirkham,
9

Jest to niepożądane z tego samego powodu, dla którego nie chcesz po prostu ślepo konwertować dowolnego typu na ciąg, gdy tylko trafi on do warstwy aplikacji. Istnieje duże prawdopodobieństwo, że będziesz chciał użyć tego obiektu w jakiś sposób przed przedstawieniem go użytkownikowi (jeśli w ogóle go przedstawisz). W tym konkretnym przykładzie wyobraź sobie, że musisz wykonać matematykę daty na obiekcie. Konwersja obiektu na ciąg znaków dokładnie przed jego wyświetleniem nie ma żadnej wady.

ogrodnik
źródło
4

Rodzaje istnieją z jakiegoś powodu, jeśli nie przyniosłyby żadnych korzyści, nie mielibyśmy ich i nie użylibyśmy ich, mielibyśmy po prostu „typ” i wszystko byłoby tak. Są nie tylko wygodne, ale także zwiększają bezpieczeństwo i wydajność. Poniżej znajduje się lista powodów, dla których należy zawsze utrzymywać typy w ich rodzimym formacie, a nie ciągi znaków . Użyłem DateTimejako przykładu przez większość czasu, ale te same zasady obowiązują dla każdego rodzaju pierwotnego, takiego jak liczby całkowite, dziesiętne, binarne itp.


Magazyn danych

Ograniczenia

Wpisz Ograniczenie

Prawie wszystkie magazyny danych pozwalają określić ograniczenia danych, w tym ograniczenia typu. Jedną z głównych zalet określenia DateTimeinstancji jest to, że przechowywane dane będą ograniczone do tego typu. Nigdy nie będzie można wprowadzić niczego poza datą i godziną, niezależnie od tego, jak dane zostały wstawione do sklepu. Ta ostatnia jest ważna w przypadku większych systemów, w których istnieje wiele procesów, które współdziałają bezpośrednio ze sklepem. Obejmuje to również próbę dodania błędnych dat, takich jak 30 lutego (dowolnego roku), ponieważ luty może mieć tylko 29 dni w roku przestępnym i 28 dni w latach przestępnych.

Ograniczenia walidacji

Istnieją również ograniczenia sprawdzania poprawności, które można wdrożyć w magazynie danych, takie jak zapewnienie, że wstawiona data nie przekracza bieżącej daty lub że data początkowa wystąpi przed datą końcową.

Operacje

Większość magazynów danych ma również wbudowane operacje / funkcje, takie jak DateAddlub DatePartw MS Sql Server. Umożliwia to rozpoczęcie filtrowania lub wybierania określonych danych, gdy dane są jeszcze w sklepie (jeszcze nie są pobierane do aplikacji).

Format powszechnie akceptowany

Korzystając z rodzimego typu, inni programiści lub systemy, które również współpracują ze sklepem, nie muszą być informowani o najdrobniejszych szczegółach dotyczących sposobu przechowywania tego pierwotnego typu. Nie dzieje się tak, jeśli ten typ był przechowywany jako ciąg znaków, musisz upewnić się, że wszyscy rozumieją format tego DateTimeciągu znaków. System ten staje się niestabilny w przypadku danych obejmujących lokalizacje, regiony i kultury pochodzenia danych, fizyczną lokalizację aplikacji oraz atrybuty użytkownika końcowego / systemu, który wchodzi w interakcję z tymi danymi. Przykład: format daty w jednym kraju może być MM / dd / rrrr (jak w USA), ale w innym może być dd / MM / rrrr, wykrycie tej różnicy staje się prawie niemożliwe.

Prędkość

Ważnymi czynnikami są również szybkość pobierania, szybkość sprawdzania poprawności, szybkość operacji i wydajność przechowywania. Przykład prędkości pobierania: magazyny danych pozwalają na indeksy na kolumnach, a indeksy te można ogólnie bardziej efektywnie wykorzystać, jeśli typ jest przechowywany w jego rodzimym formacie.

Podanie

Dostęp do danych

Wykonywanie zapytań w sklepie staje się prostsze przy użyciu natywnego systemu typów, ponieważ programiści po raz kolejny nie muszą zgadywać co do formatu pamięci. Prawie wszyscy dostawcy aplikacji do przechowywania danych ( przykład: ado.net ) zapewniają mechanizmy tworzenia odpowiednich sparametryzowanych zapytań na podstawie przekazywanych typów natywnych. Oto przykład dodania części Data do zapytania ado.net w odniesieniu do sklepu z serwerem Sql, robienie tego samego z łańcuchami byłoby bardzo uciążliwe i kruche / podatne na błędy.

command.Parameters.Add(new SqlParameter("@startDate", SqlDbType.Date) {Value = myDateInstance.Date});

Operacje

Rodzime typy w kodzie zapewniają również standardowe operacje, takie jak typ .net System.Date. Operacje mają zazwyczaj charakter matematyczny, takie jak dodawanie dat, znajdowanie różnicy między datami itp. Ponownie nie jest to łatwe do wykonania na typach łańcuchów.

Warstwa prezentacji

Widownia

Kiedy typ pierwotny zostanie ostatecznie przekonwertowany na ciąg znaków w warstwie prezentacji ( poprawne położenie stosu programu, aby to zrobić ), programista ma teraz różne opcje poprawnego wyświetlania go zgodnie z kontekstem, w którym jest prezentowany. Ten kontekst ogólnie składa się z rzeczywistego znaczenia danych i ustawień regionalnych użytkownika.

Przykład 1

Instancję daty i godziny można automatycznie sformatować na podstawie ustawień regionalnych użytkownika.

DateTime.Now.ToString("D", CultureInfo.GetCultureInfo(userContext.Culture))
Przykład 2

Wystąpienie dziesiętne może reprezentować kwotę (walutę), a ustawienia regionalne użytkownika powinny również wyświetlać kwotę zgodnie z ich preferencjami. Aplikacja c # może następnie wyświetlić wartość za pomocą

amount.ToString("C", CultureInfo.GetCultureInfo(userContext.Culture))

Może to mieć krytyczne znaczenie, ponieważ różne kultury wyświetlają liczby inaczej. W okresie amerykańskim (.) I przecinek (,) mają dokładnie odwrotne znaczenie jak w Holandii.

Lokalizacja

Jest to bardzo specyficzne dla DateTimeinstancji. Data i godzina reprezentują zdarzenie w określonym momencie, ale zwykle musi zostać przekazane / przedstawione użytkownikowi w zależności od jego własnej strefy czasowej. Przykład: DateTimeinstancja 2016-09-21T23:38:21.399Zmoże być wyświetlana jak 9/21/2016 5:21 PMdla użytkownika we wschodniej strefie czasowej w USA. Istnieje wiele sposobów osiągnięcia tego celu, ale staje się to prawie niemożliwe, jeśli instancja daty i czasu jest przechowywana w pamięci jako typ łańcucha lub w magazynie danych jako typ łańcucha.


Główna zasada

W przypadku konwersji dowolnego typu pierwotnego na reprezentację łańcuchową obowiązują 2 ogólne zasady aplikacji

  • Akceptując dane wejściowe przekonwertuj je na właściwy typ pierwotny tak wcześnie, jak to możliwe na stosie programu (zwykle w warstwie prezentacji)
  • Podczas pobierania danych, które mają zostać wyświetlone, przekonwertuj te dane na reprezentację ciągu tak późno, jak to możliwe w stosie programu (ponownie, zwykle w warstwie prezentacji)
Igor
źródło
0

Naprawdę nie ma w tym nic złego (robi się to cały czas w usługach), o ile używasz niejednoznacznego formatu daty. Przez jednoznaczność rozumiem nie tylko, że data jest jasna (np. MM / DD vs. DD / MM), ale także jaka jest strefa czasowa. Więc z góry, jeśli zamierzasz przedstawić swoje daty jako tekst, użyj formatu ISO . Zdecydowanie wolę ciągi czasowe oparte na UTC.

Plusy:

  • Ciągi daty / godziny oparte na standardach są przenośne i łatwe do zrozumienia
  • Często daty w bazach danych zawierają składnik czasu. Jeśli nie ma to znaczenia dla twoich danych, może to w rzeczywistości uprościć.

Cons:

  • Rozmiar danych Wewnętrzny format daty w bazie danych zwykle zajmuje dużo mniej miejsca niż renderowanie ciągów tej daty.
  • Zasadniczo będziesz chciał ustawić go w rzeczywistej strukturze daty lub godziny na kliencie, więc może być dodatkowy czas na analizę.

Gdyby ktoś powiedział, że chce to zrobić, zapytałbym „dlaczego?” ponieważ tak naprawdę nie ma to wiele sensu. Jeśli powodem, dla którego ktoś chce zwrócić datę jako ciąg, jest to, że po prostu wyświetli ją bezpośrednio, nie jest to dobry powód, aby używać ciągów z bazy danych.

JimmyJames
źródło