Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Rozwiązywanie problemów podczas pracy z datą i godziną w SQL Server

Baza danych Microsoft SQL Server przechowuje informacje o dacie i godzinie w różnych formatach. Najczęstsze z nich to DateTime , DataGodzina2 i data . Jak to bywa ze wszystkimi rodzajami danych, problemy mogą pojawiać się raz po raz. W tym artykule skupimy się na rozwiązywaniu niektórych z najczęstszych problemów, które możesz napotkać podczas pracy z typami danych czasu i daty SQL.

Problemy związane z regionalnie różnymi formatami dat

Format dat różni się na całym świecie. Na przykład Brytyjczycy zapisują daty w formacie dd-mm-rrrr, podczas gdy Amerykanie zapisują daty w formacie mm-dd-rrrr. W ten sposób ta sama data, 31 grudnia 2020 r., jest zapisana jako 31-12-2020 w brytyjskim formacie daty i jako 12-31-2020 w amerykańskim formacie.

Jeśli nie określisz daty w formacie odpowiadającym ustawieniom językowym Twojej instancji SQL Server, mogą pojawić się problemy z niemożnością.

Poniższy skrypt konwertuje ciąg tekstowy z informacją o dacie na format DATETIME. Ustawienia języka są ustawione na BRYTYJSKI. Ciąg tekstowy zawiera 31-12-2020 04:25:30 .

Jeśli przerzucisz ten ciąg na format DATETIME, 31 będzie traktowane jako dzień, a 12 będzie domyślnie miesiącem. W związku z tym konwersja zakończy się powodzeniem, jak pokazano z danych wyjściowych:

SET LANGUAGE BRITISH;
DECLARE @date VARCHAR(50) = '31-12-2020 04:25:30';
SELECT CAST(@date AS DATETIME);

Jeśli jednak spróbujesz przekonwertować ciąg zawierający datę 31-12-2020 do formatu DATETIME za pomocą US_ENGLISH ustawienia języka, pojawi się błąd, jak pokazano poniżej:

SET LANGUAGE US_ENGLISH;
DECLARE @date VARCHAR(50) = '31-12-2020 04:25:30';
SELECT CAST(@date AS DATETIME);

Błąd występuje, ponieważ ustawienia języka US_ENGLISH definiują 31 jako miesiąc zamiast dnia. Jako miesiąc wartość nie może być większa niż 12, otrzymujemy błąd wartości spoza zakresu .

Jeśli określisz datę jako 12-31-2020, a następnie przekonwertujesz ciąg daty na DATETIME przy użyciu ustawień US_ENGLISH, zobaczysz pomyślną konwersję:

SET LANGUAGE US_ENGLISH;
DECLARE @date VARCHAR(50) = '12-31-2020 04:25:30';
SELECT CAST(@date AS DATETIME);

Podobnie, konwertując 31-12-2020 ciąg daty w ustawieniach języka BRYTYJSKIEGO również powoduje błąd – 31 jest traktowane jako miesiąc, co nie może być.

SET LANGUAGE BRITISH;
DECLARE @date VARCHAR(50) = '12-31-2020 04:25:30';
SELECT CAST(@date AS DATETIME);

Aby dokładnie przekonwertować datę, niezależnie od ustawień języka, możesz użyć standardu ISO 8601 dla formatu daty. Aby zachować zgodność z tym standardem, określ datę jako rrrr-mm-ddThh:mm:ss .

Na przykład ciąg daty 2020-12-31T04:25:30 został pomyślnie przekonwertowany na typ danych DATETIME w ustawieniach języka BRYTYJSKIEGO:

SET LANGUAGE BRITISH;
DECLARE @date VARCHAR(50) = '2020-12-31T04:25:30';
SELECT CAST(@date AS DATETIME);

Poniższy skrypt pokazuje ten sam ciąg przekonwertowany na DATETIME z ustawieniami US_ENGLISH:

SET LANGUAGE US_ENGLISH;
DECLARE @date VARCHAR(50) = '2020-12-31T04:25:30';
SELECT CAST(@date AS DATETIME);

Zagadnienia dotyczące strefy czasowej

Możesz chcieć opracować niektóre aplikacje bazodanowe SQL Server dla odbiorców globalnych. W tym celu może być konieczne dodanie informacji o strefie czasowej do typów danych daty i godziny.

W SQL Server typ danych DATETIMEOFFSET przechowuje informacje o dacie i godzinie wraz z przesunięciem strefy czasowej. Przesunięcie strefy czasowej jest określone jako UTC +/- liczba godzin.

Na przykład poniższy skrypt używa metody SYSDATETIMEOFFSET() w celu uzyskania informacji o dacie, godzinie i przesunięciu systemu, w którym działa instancja SQL Server. Wartości zwracane przez funkcję SYSDATETIMEOFFSET() są przechowywane w zmiennej typu DATETIMEOFFSET @dateoffset. Wartość zmiennej @dateoffset jest wypisywana za pomocą instrukcji SELECT:

DECLARE @dateoffset DATETIMEOFFSET = SYSDATETIMEOFFSET();
SELECT @dateoffset

Poniższe dane wyjściowe pokazują aktualną datę i godzinę oraz wartość przesunięcia. W tym przypadku jest to +02:00.

Możesz również uzyskać tylko wartość przesunięcia ze zmiennej DATETIMEOFFSET. Aby to zrobić, musisz przekazać zmienną typu DATETIMEOFFSET jako drugą wartość parametru do funkcji DATENAME(). Pierwszym parametrem metody DATENAME() powinien być tzoffset .

Poniższy skrypt zwraca część przesunięcia czasowego bieżącej daty systemowej:

DECLARE @dateoffset DATETIMEOFFSET = SYSDATETIMEOFFSET();
SELECT DATENAME(tzoffset, @dateoffset)

Aby utworzyć niestandardową zmienną DATETIMEOFFSET, określ wartości dla części daty, godziny i przesunięcia czasu. Na przykład w poniższym skrypcie wartość daty jest 22.02.2015 , wartość części czasu jest 23:59:59:999 i wartość przesunięcia czasowego jest +05:00 .

DECLARE @dateoffset DATETIMEOFFSET = '2015-02-22 23:59:59:999 +05:00';
SELECT @dateoffset

Na koniec możesz również zaktualizować informacje o przesunięciu czasu za pomocą SWITCHOFFSET() funkcja.

Musisz zdać DATETIMEOFFSET wpisz zmienną jako pierwszą wartość parametru i przekaż nowe przesunięcie czasu jako drugą wartość parametru do SWITCHOFFSET funkcja.

Poniższy skrypt aktualizuje wartość przesunięcia czasowego dla zmiennej DATETIMEOFFSET z +05:00 do +09:00.

DECLARE @dateoffset DATETIMEOFFSET = '2015-02-22 23:59:59:999 +05:00';
SELECT SWITCHOFFSET(@dateoffset, '+09:00');

Wybieranie rekordów za pomocą operatora BETWEEN z datą i godziną

POMIĘDZY operator na serwerze SQL filtruje rekordy między zakresem wartości przekazanych do niego.

Możesz użyć operatora BETWEEN, aby zwrócić rekordy między dwiema datami. Należy jednak zachować szczególną ostrożność podczas używania go do filtrowania rekordów z datami.

Na przykład poniższy skrypt tworzy fikcyjny Hostel bazy danych i dodaje jednego Studenta stół do niego.

CREATE DATABASE Hostel

USE Hostel
CREATE TABLE Student

(
Id INT PRIMARY KEY IDENTITY(1,1),
Name VARCHAR (50) NOT NULL,
Gender VARCHAR (50),
BirthDate DateTime
)

Następny skrypt dodaje kilka fikcyjnych rekordów do Studenta stół. Data urodzenia kolumna Studenta tabela przechowuje daty. Z tego scenariusza widać, że dwie uczennice Sara i Nik mają te same daty urodzenia. Jednak czas urodzenia jest inny:

INSERT INTO Student
VALUES ('Jack', 'Male', '2017-06-30 16:30:35'),
('Sara', 'Female', '2015-02-22 00:00:00'),
('Elisa', 'Female',  '2020-03-16 22:24:39'),
('Nik', 'Male',  '2015-02-22 09:45:55'),
('Jos', 'Male',  '2015-03-25 11:55:20')

Można by pomyśleć, że operator BETWEEN może być użyty do pobrania danych wszystkich uczniów urodzonych 22.02.2015 r.

SELECT * FROM Student
WHERE BirthDate BETWEEN '2015-02-22' AND '2015-02-22'

Ale jeśli wykonasz powyższy skrypt, zobaczysz, że zwracany jest tylko jeden rekord, pomimo czasu część jest również wliczony w cenę.

Powodem jest to, że operator BETWEEN domyślnie traktuje wartość DATETIME z 2015-02-22 jako 2015-02-22 00:00:00 . Dlatego operator BETWEEN w powyższym zapytaniu wyszukał rekordy z Data urodzenia wartość między 2015-02-22 00:00:00 a 2015-02-22 00:00:00 .

Aby rozwiązać ten problem, musimy określić część czasu podczas korzystania z operatora BETWEEN z typem danych DATETIME.

Poniższy skrypt zwróci wszystkie rekordy między 22.02.2015 r 00:00:00 i 22.02.2015 23:59:59:999 . Czas dla górnego limitu daty to 23:59:999.

SELECT * FROM Student
WHERE BirthDate BETWEEN '2015-02-22' AND '2015-02-22 23:59:59:999';

W wyniku otrzymujemy dwa rekordy dla Data urodzenia 22.02.2015 .

Problemy związane z zakresami dat

Typ danych DATETIME obsługuje tylko lata od 1753 do 9999. Dlatego, jeśli spróbujesz zapisać datę z wartością roku większą niż 9999 lub mniejszą niż 1753, otrzymasz błąd.

Poniższy skrypt próbuje przekonwertować 1392-12-31 ciąg daty. 1392 to mniej niż 1753. W związku z tym mamy błąd wartości spoza zakresu.

DECLARE @date VARCHAR(50) = '1392-12-31 04:25:30';
SELECT CAST(@date AS DATETIME);

Do przechowywania wartości roku mniej niż 1753 , możesz użyć DATETIME2 typ danych. Przechowuje wartości roku od 0000 do 9999.

Poniższy skrypt pomyślnie konwertuje ciąg daty 1392-12-31 na typ danych DATETIME2:

DECLARE @date VARCHAR(50) = '1392-12-31 04:25:30';
SELECT CAST(@date AS DATETIME2);

Używanie TRY_COVERT do konwersji daty i godziny

Funkcja CONVERT w SQL Server konwertuje dane z jednego typu na inny. Możesz go użyć do konwersji formatów danych typu data na inne formaty i odwrotnie. Jeśli jednak konwersja się nie powiedzie, funkcja CONVERT wygeneruje błąd.

Na przykład konwertujemy ciąg 31-31-2015 do formatu DATETIME:

DECLARE @date VARCHAR(50) = '2015-31-13';
SELECT  CONVERT(DATETIME, @date ,105) as DOB_CONV

Jeśli chcesz, aby zamiast komunikatu o błędzie zwracana była wartość NULL, gdy konwersja nie powiedzie się, użyj TRY_CONVERT funkcjonować. Ta metoda nie spowoduje awarii aplikacji – po prostu zwraca wartość NULL.

DECLARE @date VARCHAR(50) = '2015-31-13';

SELECT  TRY_CONVERT(DATETIME, @date ,105) as DOB_CONV

Wniosek

Pracując z SQL Server, możesz napotkać wiele problemów psujących Twoje doświadczenie i komplikujących zadania. Z drugiej strony znajomość najczęstszych problemów jest najskuteczniejszą metodą zapobiegania im. Dlatego poświęciliśmy ten artykuł rozwiązywaniu takich niedogodności, które mogą wystąpić podczas pracy z informacjami o datach i godzinach.

Należy również pamiętać, że nowoczesne narzędzia do pracy z bazami danych SQL Server mogą znacznie uprościć życie specjalistów DB. W szczególności dbForge Studio dla SQL Server zapewnia funkcję Visual Data Editor do zastosowania w przypadku dat. Możesz go używać do przeglądania i edycji dat w najbardziej przyjazny dla użytkownika sposób.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. T-SQL — aliasowanie przy użyciu =kontra as

  2. 4 sposoby na uzyskanie listy harmonogramów w programie SQL Server Agent (T-SQL)

  3. Dlaczego odczyty logiczne dla okienkowych funkcji agregujących są tak wysokie?

  4. Jak podzielić ciąg, aby uzyskać dostęp do elementu x?

  5. Kopia zapasowa SQL Server 2017 -1