Почему сервер SQL не позволяет мне сохранять дату 21/04/17 в качестве даты? - PullRequest
0 голосов
/ 26 марта 2020

У меня есть таблица, в которой в настоящее время все столбцы хранятся как nvarchar (max), поэтому я конвертирую все типы данных, чтобы они стали такими, какими они должны быть. У меня есть столбец дат, однако, когда я запускаю это:

ALTER TABLE Leavers ALTER COLUMN [Actual_Termination_Date] date;

Я получаю

"Conversion failed when converting date and/or time from character string".

Это относительно нормально, поэтому я сделал следующее расследование:

SELECT DISTINCT TOP 20 [Actual_Termination_Date]
FROM LEAVERS
WHERE ISDATE([Actual_Termination_Date]) = 0

, который возвратил:

NULL
13/04/2017
14/04/2017
17/04/2017
19/04/2017
21/04/2017
23/04/2017
24/04/2017
26/04/2017
28/04/2017
29/03/2017
29/04/2017
30/04/2017
31/03/2017
42795
42797
42813
42817
42820
42825

Нулевой и превосходный формат даты стиля (например, 42795) не проблема, однако это те, которые выглядят как совершенно нормальные даты, с которыми у меня проблемы. Я обычно исправляю подобные проблемы, используя одно из следующих исправлений:

SELECT  cast([Actual_Termination_Date] - 2 as datetime)
FROM LEAVERS
WHERE ISDATE([Actual_Termination_Date]) = 0

или

SELECT  cast(convert(nvarchar,[Actual_Termination_Date], 103) - 2 as datetime)
FROM LEAVERS
WHERE ISDATE([Actual_Termination_Date]) = 0

Когда они возвращают даты, как я ожидал, я бы тогда сделал Оператор UPDATE, чтобы изменить их в таблице, а затем преобразовать тип столбца. Однако я получаю сообщение об ошибке, в котором говорится, что различные даты не могут быть преобразованы, например:

Conversion failed when converting the nvarchar value '21/04/2017' to data type int.

Есть мысли? Спасибо!

Ответы [ 4 ]

5 голосов
/ 26 марта 2020

Возможно, из-за вашей языковой настройки. Чтобы '21/04/2017' работал, вам нужно использовать язык BRITISH или другой язык, который использует dd/MM/yyyy. Я подозреваю, вы используете ENGLISH, который на самом деле является американским.

Американец использует MM/dd/yyyy, что означает, что '21/04/2017' будет означать 4-й день 21-го месяца в 2017 году; очевидно, это не работает.

лучший метод заключается в использовании однозначного формата, независимо от языка и типа данных. Для SQL Сервера это yyyyMMdd и yyyy-MM-ddThh:mm:ss.nnnnnnn (yyyy-MM-dd и yyyy-MM-dd hh:mm:ss.nnnnnnn не однозначны в SQL Server при использовании более старых типов данных datetime и smalldatetime).

В противном случае вы можете использовать CONVERT с кодом стиля :

SELECT CONVERT(date,'21/04/2017', 103)

Однако проблема с вашими данными заключается в том, что у вас есть значения, которые находятся в формат dd/MM/yyyy и целочисленные значения. Значение int (не varchar) 42817 как datetime в SQL Server равно 2017-03-25. С другой стороны, если эти данные пришли из Excel, тогда значение равно 2017-03-23. Я собираюсь предположить, данные поступили из Excel, а не SQL Сервер (потому что драйверы ACE имеют привычку считывать даты в виде чисел, потому что они не являются "асом").

Поэтому вам нужно сначала преобразовать значения в однозначный формат, так что это будет yyyyMMdd. Поскольку у нас есть 2 различных типа значений, это немного сложнее, но все же возможно:

UPDATE dbo.Leavers
SET Actual_Termination_Date = CONVERT(varchar(8), ISNULL(TRY_CONVERT(date, Actual_Termination_Date, 103), DATEADD(DAY, TRY_CONVERT(int, Actual_Termination_Date),'18991230')), 112);

Затем вы можете изменить свою таблицу:

ALTER TABLE dbo.Leavers ALTER COLUMN [Actual_Termination_Date] date;

DB <> Скрипка с использованием выражения DML Михаила Турчина.

2 голосов
/ 26 марта 2020

Поместите столбец в канонический формат сначала , затем преобразуйте:

update leavers
    set Actual_Termination_Date = try_convert(date, [Actual_Termination_Date], 103);

ALTER TABLE Leavers ALTER COLUMN [Actual_Termination_Date] date;

update будет выполнять неявное преобразование даты в строку. alter должен иметь возможность "отменить" это неявное преобразование.

Сделайте резервную копию таблицы, прежде чем сделать это! Вы, вероятно, обнаружите, что некоторые даты недопустимы - это правило, когда вы сохраняете даты в виде строк, хотя в небольшом меньшинстве случаев все строки дат фактически имеют одинаковый формат.

1 голос
/ 26 марта 2020

Фактическая дата не имеет значения. Ошибка возникает при попытке вычесть 2 из строки:

[Actual_Termination_Date] - 2

Подсказка приходит из сообщения об ошибке:

Преобразование не удалось при преобразовании значения nvarchar '21 / 04 / 2017 ' для типа данных int .

Чтобы устранить проблему, используйте DATEADD после преобразования:

SELECT  DATEADD(days, -2, convert(datetime, [Actual_Termination_Date], 103))
0 голосов
/ 26 марта 2020

У вас просто есть несовместимый формат даты в вашем столбце, что ужасно.

Неправильный тип данных приводит к этому, поэтому так важно иметь правильные типы данных для столбцов.

Давайте немного разберемся с этим:

-- some test data
declare @tbl table (dt varchar(20));
insert into @tbl values 
(NULL),
('13/04/2017'),
('14/04/2017'),
('17/04/2017'),
('19/04/2017'),
('21/04/2017'),
('23/04/2017'),
('24/04/2017'),
('26/04/2017'),
('28/04/2017'),
('29/03/2017'),
('29/04/2017'),
('30/04/2017'),
('31/03/2017'),
('42795'),
('42797'),
('42813'),
('42817'),
('42820'),
('42825');
-- here we handle one format
select convert(date, dt, 103) from @tbl
where len(dt) > 5
or dt is null
-- here we handle excel like format
select dateadd(day, cast(dt as int), '1900-01-01') from @tbl
where len(dt) = 5

Итак, как вы видите, вы должны применять различные подходы для выполнения этой задачи. CASE WHEN оператор должен хорошо вписываться, см. Ниже SELECT:

select case when len(dt) = 5 then
         dateadd(day, cast(dt as int), '1900-01-01')
       else convert(date, dt, 103) end
from @tbl
...