Konwersja ciągu znaków SQL Server na datę


186

Chcę przekonwertować ciąg taki jak ten:

'10/15/2008 10:06:32 PM'

na równoważną wartość DATETIME w Sql Server.

W Oracle powiedziałbym tak:

TO_DATE('10/15/2008 10:06:32 PM','MM/DD/YYYY HH:MI:SS AM')

To pytanie sugeruje, że muszę przeanalizować ciąg znaków do jednego ze standardowych formatów , a następnie przekonwertować przy użyciu jednego z tych kodów. To wydaje się niedorzeczne w przypadku tak przyziemnej operacji. Czy istnieje prostszy sposób?


Odpowiedzi:


28

SQL Server (2005, 2000, 7.0) nie ma żadnego elastycznego, a nawet nieelastycznego sposobu na przyjęcie arbitralnie uporządkowanej daty i godziny w formacie łańcuchowym i przekonwertowanie jej na typ danych.

Przez „arbitralnie” rozumiem „formę, którą osoba, która to napisała, choć być może nie ty, ja ani ktoś z drugiej strony planety, uważałby ją za intuicyjną i całkowicie oczywistą”. Szczerze mówiąc, nie jestem pewien, czy istnieje taki algorytm.


32
istnieje taki algorytm, Oracle już go zaimplementował, a brak odpowiednika programu SQL Server to ciągły ból.
matao

19
@ matatao, więc proszę nas oświecić, w jaki sposób Oracle magicznie określa, czy użytkownik, który napisał, 9/6/12oznaczał 6 września 2012 r., 9 czerwca 2012 r., 6 grudnia 2009 r., czy coś innego?
Aaron Bertrand,

13
nie martw się, tutaj: techonthenet.com/oracle/functions/to_date.php Oczywiście musi to być spójny format określony przez programistę, ale znacznie bardziej elastyczny niż garść masek formatu, które oferuje MS, co powoduje bolesne niestandardowe parsowanie .
matao

3
@JosphStyons był świadomy funkcji TO_DATE Oracle, jak pokazano w jego próbce. Chciał wiedzieć, czy istnieje sposób konwersji dat na ciągi bez konieczności znajomości formatu / struktury ciągu. SQL tego nie robi i na pewno wydaje się, że TO_DATE Oracle również tego nie robi.
Philip Kelley,

23
@ PhilipKelley Nie widzę, gdzie OP chce wiedzieć, jak to zrobić bez znajomości formatu. Mówi wprost, że zna format i pyta, czy SQL Server ma coś równoważnego TO_DATE, tj. Coś, co pozwala deweloperowi wprowadzić dowolny ciąg formatu.
neverfox

306

Spróbuj tego

Cast('7/7/2011' as datetime)

i

Convert(varchar(30),'7/7/2011',102)

Aby uzyskać więcej informacji, zobacz CAST i CONVERT (Transact-SQL) .


14
Jest to właściwy sposób na zrobienie tego i powinien być oznaczony jako poprawna odpowiedź. Pamiętaj, że to Cast('2011-07-07' as datetime)również działa i eliminuje dwuznaczność w stosunku do zamówień z miesiąca na dzień.
Joe DeRose

To nie działa, gdy miesiąc jest> 12. Formatowanie oczekuje formatu mm / dd / rrrr
Chakri

Użyj Konwertuj (varchar (30), „7/7/2011”, 103) podczas konwersji z dd / mm / rrrr
Matias Masso

2
@Chakri, jeśli daty są w dd / mm / rrrr, użyj SET DATEFORMAT dmyprzed zapytaniem
Nathan Griffiths

49

Uruchom to przez procesor zapytań. Formatuje daty i / lub godziny, a jeden z nich powinien dać ci to, czego szukasz. Dostosowanie nie będzie trudne:

Declare @d datetime
select @d = getdate()

select @d as OriginalDate,
convert(varchar,@d,100) as ConvertedDate,
100 as FormatValue,
'mon dd yyyy hh:miAM (or PM)' as OutputFormat
union all
select @d,convert(varchar,@d,101),101,'mm/dd/yy'
union all
select @d,convert(varchar,@d,102),102,'yy.mm.dd'
union all
select @d,convert(varchar,@d,103),103,'dd/mm/yy'
union all
select @d,convert(varchar,@d,104),104,'dd.mm.yy'
union all
select @d,convert(varchar,@d,105),105,'dd-mm-yy'
union all
select @d,convert(varchar,@d,106),106,'dd mon yy'
union all
select @d,convert(varchar,@d,107),107,'Mon dd, yy'
union all
select @d,convert(varchar,@d,108),108,'hh:mm:ss'
union all
select @d,convert(varchar,@d,109),109,'mon dd yyyy hh:mi:ss:mmmAM (or PM)'
union all
select @d,convert(varchar,@d,110),110,'mm-dd-yy'
union all
select @d,convert(varchar,@d,111),111,'yy/mm/dd'
union all
select @d,convert(varchar,@d,12),12,'yymmdd'
union all
select @d,convert(varchar,@d,112),112,'yyyymmdd'
union all
select @d,convert(varchar,@d,113),113,'dd mon yyyy hh:mm:ss:mmm(24h)'
union all
select @d,convert(varchar,@d,114),114,'hh:mi:ss:mmm(24h)'
union all
select @d,convert(varchar,@d,120),120,'yyyy-mm-dd hh:mi:ss(24h)'
union all
select @d,convert(varchar,@d,121),121,'yyyy-mm-dd hh:mi:ss.mmm(24h)'
union all
select @d,convert(varchar,@d,126),126,'yyyy-mm-dd Thh:mm:ss:mmm(no spaces)'

47

W SQL Server Denali będziesz mógł zrobić coś, co zbliży się do tego, czego szukasz. Ale nadal nie można po prostu przekazać dowolnego arbitralnie zdefiniowanego ciągu niepoprawnej daty i oczekiwać, że SQL Server go dostosuje. Oto jeden przykład z wykorzystaniem czegoś, co opublikowałeś we własnej odpowiedzi. Funkcja FORMAT () i może również akceptować ustawienia narodowe jako opcjonalny argument - opiera się na formacie .Net, więc większość, jeśli nie wszystkie formaty tokenów, które można zobaczyć, będą dostępne.

DECLARE @d DATETIME = '2008-10-13 18:45:19';

-- returns Oct-13/2008 18:45:19:
SELECT FORMAT(@d, N'MMM-dd/yyyy HH:mm:ss');

-- returns NULL if the conversion fails:
SELECT TRY_PARSE(FORMAT(@d, N'MMM-dd/yyyy HH:mm:ss') AS DATETIME);

-- returns an error if the conversion fails:
SELECT PARSE(FORMAT(@d, N'MMM-dd/yyyy HH:mm:ss') AS DATETIME);

Gorąco zachęcam do przejęcia większej kontroli i oczyszczenia danych wejściowych. Dni, w których ludzie mogą wpisywać daty w dowolnym formacie w dowolnym polu formularza, powinny być już za nami. Jeśli ktoś wejdzie 8/9/2011, czy to jest 9 sierpnia czy 8 września? Jeśli sprawisz, że wybiorą datę na kontrolce kalendarza, aplikacja będzie mogła kontrolować format. Bez względu na to, ile próbujesz przewidzieć zachowanie użytkowników, zawsze wymyślą głupszy sposób na wpisanie daty, której nie planowałeś.

Jednak do Denali uważam, że @Ovidiu ma jak dotąd najlepszą radę ... można to uczynić dość trywialnym poprzez wdrożenie własnej funkcji CLR. Następnie możesz napisać skrzynkę / przełącznik dla dowolnej liczby zwariowanych niestandardowych formatów.


AKTUALIZACJA dla @herhert :

SELECT TRY_PARSE('10/15/2008 10:06:32 PM' AS DATETIME USING 'en-us');
SELECT TRY_PARSE('15/10/2008 10:06:32 PM' AS DATETIME USING 'en-gb');

Wyniki:

2008-10-15 22:06:32.000
2008-10-15 22:06:32.000

Nadal musisz mieć tę inną kluczową informację w pierwszej kolejności. Nie można użyć natywnego języka T-SQL do ustalenia, czy 6/9/2012jest to 9 czerwca, czy 6 września.


1
Myślę, że pytanie brzmiało, jak przekonwertować ciąg na datę, a nie godzinę na ciąg.
David Hergert

1
TRY_PARSE było idealne. Wystąpił problem z analizowaniem daty „czw 22 września 2016”, dziękuję za udostępnienie!
Simon

11

W przypadku tego problemu najlepszym rozwiązaniem, którego używam, jest posiadanie funkcji CLR w Sql Server 2005, która korzysta z jednej z funkcji DateTime.Parse lub ParseExact, aby zwrócić wartość DateTime w określonym formacie.



8

dlaczego nie spróbować

select convert(date,'10/15/2011 00:00:00',104) as [MM/dd/YYYY]

formaty dat można znaleźć w SQL Server Helper> SQL Server Date Formats


Twój przykładowy kod nie działa. „Konwersja nie powiodła się podczas konwersji daty i / lub godziny z ciągu znaków.”
César León

5
Powinno być select convert(date,'10/15/2011 00:00:00',101). Więcej informacji o formacie i dlaczego 101, na docs.microsoft.com/en-us/sql/t-sql/functions/…
inny Użytkownik

1
Osiem osób głosowało za tą odpowiedzią, która nawet nie działa ...
David Klempfner,

4

Zajęło mi to chwilę, aby to rozgryźć, więc na wypadek, gdyby to mogło komuś pomóc:

W SQL Server 2012 i lepszych możesz użyć tej funkcji:

SELECT DATEFROMPARTS(2013, 8, 19);

Oto, jak skończyłem wyodrębnianie części daty, aby wprowadzić tę funkcję:

select
DATEFROMPARTS(right(cms.projectedInstallDate,4),left(cms.ProjectedInstallDate,2),right( left(cms.ProjectedInstallDate,5),2)) as 'dateFromParts'
from MyTable

3

Na tej stronie znajdują się odniesienia do wszystkich określonych konwersji daty i godziny dostępnych dla funkcji CONVERT. Jeśli twoje wartości nie mieszczą się w jednym z akceptowalnych wzorców, myślę, że najlepszą rzeczą jest pójście drogą ParseExact.


link jest zepsuty.
Michael Potter

3

Osobiście, jeśli masz do czynienia z dowolnymi lub całkowicie nieużytecznymi formatami, pod warunkiem, że wiesz, co jest z wyprzedzeniem lub będzie, po prostu użyj wyrażenia regularnego, aby wyciągnąć sekcje z żądanej daty i utworzyć prawidłowy składnik data / data / godzina.


1

Wiem, że to zły, stary post z mnóstwem odpowiedzi, ale wiele osób myśli, że POTRZEBUJĄ albo rozłożyć rzeczy na części i złożyć je z powrotem, albo twierdzą, że nie ma sposobu, aby pośrednio wykonać konwersję, o którą poprosił oryginał OP .

Aby przejrzeć i, miejmy nadzieję, udzielić łatwej odpowiedzi innym osobom z tym samym pytaniem, PO zapytał, jak przekonwertować „10/15/2008 10:06:32 PM” na DATETIME. Teraz SQL Server ma pewne zależności językowe do konwersji czasowych, ale jeśli językiem jest angielski lub coś podobnego, staje się to prostym problemem ... po prostu wykonaj konwersję i nie martw się o format. Na przykład (i możesz użyć CONVERT lub CAST) ...

 SELECT UsingCONVERT = CONVERT(DATETIME,'10/15/2008 10:06:32 PM')
        ,UsingCAST   = CAST('10/15/2008 10:06:32 PM' AS DATETIME)
;

... i to daje następujące odpowiedzi, które są poprawne.

wprowadź opis zdjęcia tutaj

Jak mówią w reklamach telewizyjnych: „Ale czekaj! Nie zamawiaj jeszcze! ​​Bez dodatkowych kosztów może zrobić DUŻO więcej!”

Zobaczmy prawdziwą moc czasowych konwersji z DATETIME i częściowo zbadajmy błąd znany jako DATETIME2. Sprawdź niesamowite formaty, które DATETIME może obsługiwać automatycznie, a których DATETIME2 nie. Uruchom następujący kod i zobacz ...

--===== Set the language for this example.
    SET LANGUAGE ENGLISH --Same a US-English
;
--===== Use a table constructor as if it were a table for this example.
 SELECT *
        ,DateTimeCONVERT  = TRY_CONVERT(DATETIME,StringDT)
        ,DateTimeCAST     = TRY_CAST(StringDT AS DATETIME)
        ,DateTime2CONVERT = TRY_CONVERT(DATETIME2,StringDT)
        ,DateTime2CAST    = TRY_CAST(StringDT AS DATETIME2)
   FROM (
         VALUES
         ('Same Format As In The OP'    ,'12/16/2001 01:51:01 PM')
        ,('Almost Normal'               ,'16 December, 2001 1:51:01 PM')
        ,('More Normal'                 ,'December 16, 2001 01:51:01 PM')
        ,('Time Up Front + Spaces'      ,'   13:51:01  16 December   2001')
        ,('Totally Whacky Format #01'   ,'  16  13:51:01  December   2001')
        ,('Totally Whacky Format #02'   ,'  16    December 13:51:01  2001  ')
        ,('Totally Whacky Format #03'   ,'  16    December 01:51:01  PM 2001  ')
        ,('Totally Whacky Format #04'   ,' 2001 16    December 01:51:01  PM ')
        ,('Totally Whacky Format #05'   ,' 2001    December 01:51:01  PM  16  ')
        ,('Totally Whacky Format #06'   ,' 2001 16    December  01:51:01 PM  ')
        ,('Totally Whacky Format #07'   ,' 2001 16    December  13:51:01 PM  ')
        ,('Totally Whacky Format #08'   ,' 2001 16  13:51:01 PM  December    ')
        ,('Totally Whacky Format #09'   ,'   13:51:01   PM  2001.12/16 ')
        ,('Totally Whacky Format #10'   ,'   13:51:01   PM  2001.December/16 ')
        ,('Totally Whacky Format #11'   ,'   13:51:01   PM  2001.Dec/16 ')
        ,('Totally Whacky Format #12'   ,'   13:51:01   PM  2001.Dec.16 ')
        ,('Totally Whacky Format #13'   ,'   13:51:01   PM  2001/Dec.16')
        ,('Totally Whacky Format #14'   ,'   13:51:01   PM  2001 . 12/16 ')
        ,('Totally Whacky Format #15'   ,'   13:51:01   PM  2001 . December / 16 ')
        ,('Totally Whacky Format #16'   ,'   13:51:01   PM  2001 . Dec /   16 ')
        ,('Totally Whacky Format #17'   ,'   13:51:01   PM  2001 . Dec .   16 ')
        ,('Totally Whacky Format #18'   ,'   13:51:01   PM  2001 / Dec .   16')
        ,('Totally Whacky Format #19'   ,'   13:51:01   PM  2001 . Dec -   16 ')
        ,('Totally Whacky Format #20'   ,'   13:51:01   PM  2001 - Dec -   16 ')
        ,('Totally Whacky Format #21'   ,'   13:51:01   PM  2001 - Dec .   16')
        ,('Totally Whacky Format #22'   ,'   13:51:01   PM  2001 - Dec /   16 ')
        ,('Totally Whacky Format #23'   ,'   13:51:01   PM  2001 / Dec -   16')
        ,('Just the year'               ,' 2001      ')
        ,('YYYYMM'                      ,' 200112      ')
        ,('YYYY MMM'                    ,'2001 Dec')
        ,('YYYY-MMM'                    ,'2001-Dec')
        ,('YYYY    .     MMM'           ,'2001    .     Dec')
        ,('YYYY    /     MMM'           ,'2001    /     Dec')
        ,('YYYY    -     MMM'           ,'2001    /     Dec')
        ,('Forgot The Spaces #1'        ,'2001December26')
        ,('Forgot The Spaces #2'        ,'2001Dec26')
        ,('Forgot The Spaces #3'        ,'26December2001')
        ,('Forgot The Spaces #4'        ,'26Dec2001')
        ,('Forgot The Spaces #5'        ,'26Dec2001 13:51:01')
        ,('Forgot The Spaces #6'        ,'26Dec2001 13:51:01PM')
        ,('Oddly, this doesn''t work'   ,'2001-12')
        ,('Oddly, this doesn''t work'   ,'12-2001')
        ) v (Description,StringDT)
;

Tak, tak ... SQL Server faktycznie ma dość elastyczną metodę obsługi wszelkiego rodzaju dziwnych formatów czasowych i nie jest wymagana żadna specjalna obsługa. Nie musieliśmy nawet usuwać „PM” dodanych do 24-godzinnych czasów. To „PFM” (Pure Freakin 'Magic).

Rzeczy będą się nieco różnić w zależności od JĘZYKA, który wybrałeś dla swojego serwera, ale cała jego część będzie obsługiwana w obu kierunkach.

A te „auto-magiczne” konwersje nie są niczym nowym. Cofają się naprawdę daleko.


Zła nowa odpowiedź na niegodziwe stare pytanie. Dzięki!
JosephStyons

Dzięki za opinie, @JosephStyons.
Jeff Moden

0

Jeśli chcesz, aby SQL Server próbował to rozgryźć, po prostu użyj CAST CAST („jakikolwiek” AS datetime). To jednak ogólnie zły pomysł. Pojawią się problemy z międzynarodowymi datami. Jak już odkryłeś, aby uniknąć tych problemów, chcesz użyć kanonicznego formatu daty ODBC. To jest format numer 120, 20 jest formatem tylko dwucyfrowym. Nie sądzę, że SQL Server ma wbudowaną funkcję, która pozwala na podanie formatu określonego przez użytkownika. Możesz napisać własny, a może nawet go znaleźć, jeśli będziesz szukać w Internecie.


Jeśli masz międzynarodowe daty w jednej kolumnie, absolutnie zgadzam się, że użycie numeru formatu jest dobrym pomysłem. Jeśli masz daty międzynarodowe i daty amerykańskie zmieszane w jednej kolumnie, po prostu nie ma sposobu, aby określić różnicę między czymś takim jak 7/6/2000 i 6/7/2000, chyba że masz siostrzaną kolumnę, która wyjaśnia format. Dlatego jakość danych u źródła MUSI być czymś. Jeśli WIESZ, że masz, powiedzmy, wszystkie amerykańskie daty, pozwól niejawnym konwersjom zrobić swoje. Jeśli zawiodą, to wiesz na pewno, że coś w kolumnie musi zostać naprawione.
Jeff Moden

0

pośrednio przekonwertować ciąg na datetime w MSSQL

create table tmp 
(
  ENTRYDATETIME datetime
);

insert into tmp (ENTRYDATETIME) values (getdate());
insert into tmp (ENTRYDATETIME) values ('20190101');  --convert string 'yyyymmdd' to datetime


select * from tmp where ENTRYDATETIME > '20190925'  --yyyymmdd 
select * from tmp where ENTRYDATETIME > '20190925 12:11:09.555'--yyyymmdd HH:MIN:SS:MS



Cześć, witamy w Stackoverflow i dziękujemy za odpowiedź. Chociaż ten kod może odpowiedzieć na pytanie, czy możesz rozważyć dodanie wyjaśnienia problemu, który rozwiązałeś i jak go rozwiązać? Pomoże to przyszłym czytelnikom lepiej zrozumieć twoją odpowiedź i wyciągnąć z niej wnioski.
Plutian

-4
dateadd(day,0,'10/15/2008 10:06:32 PM')

3
Witamy w StackOverflow! Proszę edytować swoją odpowiedź do dodania wyjaśnień na kodzie. To pytanie ma prawie jedenaście lat i ma już wiele dobrze wyjaśnionych, pozytywnych odpowiedzi. Bez wyjaśnienia w swojej odpowiedzi, że to o wiele gorszej jakości w porównaniu do tych innych i najprawdopodobniej się downvoted lub usunięta. Dodanie tego wyjaśnienia pomoże uzasadnić istnienie twojej odpowiedzi tutaj.
Das_Geek

Tak, ale „dobrze wyjaśnione” posty, nawet te wysoko ocenione, są zbyt skomplikowane. Ten zamieszczony tutaj jest tak naprawdę jednym z lepszych, z wyjaśnieniem lub bez,
Jeff Moden
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.