为什么 SQL 服务器不让我将“21/04/17”存储为日期?
Why isn't SQL Server letting me store '21/04/17' as a date?
我有一个 table 当前所有列都存储为 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
return编辑:
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
null 和 excel 样式的日期格式(例如 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
当这些 return 像我预期的那样返回日期时,我会执行更新语句以在 table 中更改它们,然后转换列类型。但是,我不断收到一条错误消息,告诉我无法转换各种日期,例如:
Conversion failed when converting the nvarchar value '21/04/2017' to data type int.
有什么想法吗?谢谢!
可能是因为您的语言设置。要使 '21/04/2017'
正常工作,您需要使用 BRITISH
语言或使用 dd/MM/yyyy
的其他语言。我怀疑你用的是ENGLISH
,实际上是美国人。
美国人用MM/dd/yyyy
表示'21/04/2017'
表示2017年第21个月的第4天;显然那是行不通的。
best方法是使用明确的格式,无论语言和数据类型如何。对于 yyyyMMdd
和 yyyy-MM-ddThh:mm:ss.nnnnnnn
的 SQL 服务器(yyyy-MM-dd
和 yyyy-MM-dd hh:mm:ss.nnnnnnn
在 SQL 中是 而不是 明确的使用旧 datetime
和 smalldatetime
数据类型时的服务器)。
否则,您可以使用 CONVERT
和 style 代码:
SELECT CONVERT(date,'21/04/2017', 103)
但是,您的数据存在的问题是,您的值的格式为 dd/MM/yyyy
和整数值。 int
(不是 varchar
)值 42817
作为 SQL 服务器中的 datetime
是 2017-03-25
。另一方面,如果此数据来自 Excel,则值为 2017-03-23
。我将 假设 数据来自 Excel,而不是 SQL 服务器(因为 ACE 驱动程序习惯于将日期读取为数字,因为它们不是 "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);
然后你可以改变你的table:
ALTER TABLE dbo.Leavers ALTER COLUMN [Actual_Termination_Date] date;
DB<>Fiddle 使用 Michał Turczyn 的 DML 语句。
先将列转换为规范格式,然后转换:
update leavers
set Actual_Termination_Date = try_convert(date, [Actual_Termination_Date], 103);
ALTER TABLE Leavers ALTER COLUMN [Actual_Termination_Date] date;
update
将执行从日期到字符串的隐式转换。 alter
应该可以"undo"那个隐式转换。
在执行此操作之前备份 table!您可能会发现某些日期无效——这几乎是将日期存储为字符串时的规则,尽管在少数情况下,所有日期字符串的格式实际上都是一致的。
实际日期无关紧要。当您尝试从字符串中减去 2 时会发生错误:
[Actual_Termination_Date] - 2
线索来自错误信息:
Conversion failed when converting the nvarchar value '21/04/2017' to data type int.
要解决此问题,请在转换后使用 DATEADD
:
SELECT DATEADD(days, -2, convert(datetime, [Actual_Termination_Date], 103))
您的列中的日期格式不一致,这很糟糕。
错误的数据类型导致了它,这就是为什么在列上使用正确的数据类型如此重要。
让我们稍微调查一下:
-- 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
我有一个 table 当前所有列都存储为 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
return编辑:
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
null 和 excel 样式的日期格式(例如 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
当这些 return 像我预期的那样返回日期时,我会执行更新语句以在 table 中更改它们,然后转换列类型。但是,我不断收到一条错误消息,告诉我无法转换各种日期,例如:
Conversion failed when converting the nvarchar value '21/04/2017' to data type int.
有什么想法吗?谢谢!
可能是因为您的语言设置。要使 '21/04/2017'
正常工作,您需要使用 BRITISH
语言或使用 dd/MM/yyyy
的其他语言。我怀疑你用的是ENGLISH
,实际上是美国人。
美国人用MM/dd/yyyy
表示'21/04/2017'
表示2017年第21个月的第4天;显然那是行不通的。
best方法是使用明确的格式,无论语言和数据类型如何。对于 yyyyMMdd
和 yyyy-MM-ddThh:mm:ss.nnnnnnn
的 SQL 服务器(yyyy-MM-dd
和 yyyy-MM-dd hh:mm:ss.nnnnnnn
在 SQL 中是 而不是 明确的使用旧 datetime
和 smalldatetime
数据类型时的服务器)。
否则,您可以使用 CONVERT
和 style 代码:
SELECT CONVERT(date,'21/04/2017', 103)
但是,您的数据存在的问题是,您的值的格式为 dd/MM/yyyy
和整数值。 int
(不是 varchar
)值 42817
作为 SQL 服务器中的 datetime
是 2017-03-25
。另一方面,如果此数据来自 Excel,则值为 2017-03-23
。我将 假设 数据来自 Excel,而不是 SQL 服务器(因为 ACE 驱动程序习惯于将日期读取为数字,因为它们不是 "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);
然后你可以改变你的table:
ALTER TABLE dbo.Leavers ALTER COLUMN [Actual_Termination_Date] date;
DB<>Fiddle 使用 Michał Turczyn 的 DML 语句。
先将列转换为规范格式,然后转换:
update leavers
set Actual_Termination_Date = try_convert(date, [Actual_Termination_Date], 103);
ALTER TABLE Leavers ALTER COLUMN [Actual_Termination_Date] date;
update
将执行从日期到字符串的隐式转换。 alter
应该可以"undo"那个隐式转换。
在执行此操作之前备份 table!您可能会发现某些日期无效——这几乎是将日期存储为字符串时的规则,尽管在少数情况下,所有日期字符串的格式实际上都是一致的。
实际日期无关紧要。当您尝试从字符串中减去 2 时会发生错误:
[Actual_Termination_Date] - 2
线索来自错误信息:
Conversion failed when converting the nvarchar value '21/04/2017' to data type int.
要解决此问题,请在转换后使用 DATEADD
:
SELECT DATEADD(days, -2, convert(datetime, [Actual_Termination_Date], 103))
您的列中的日期格式不一致,这很糟糕。
错误的数据类型导致了它,这就是为什么在列上使用正确的数据类型如此重要。
让我们稍微调查一下:
-- 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