【问题标题】:Convert "nvarchar" column data type to "DateTime'将“nvarchar”列数据类型转换为“DateTime”
【发布时间】:2020-10-27 08:15:48
【问题描述】:

有人知道如何在 SQL Server 中将列数据类型从“nvarchar”格式转换为“datetime”吗?

例如:

  1. 01-06-2020 12:00:00 AM
  2. 6-17-2020 12:00:00 AM

我尝试了以下查询:

ALTER TABLE MyTable ALTER COLUMN UpdatedDate datetime

但是,出现以下错误

将 nvarchar 数据类型转换为 datetime 数据类型导致值超出范围。

【问题讨论】:

  • 你的价值观不好。

标签: sql sql-server


【解决方案1】:

好吧,既然您已经看到它可能存在的问题,我希望您已经学会了永远不要将 DateTime 值存储为字符串。
您应该努力始终使用最合适的可用数据类型。
不过不用担心。这是一个常见的错误 Aaron Bertrand 写了一篇关于它的博客文章(嗯,更普遍的问题) - 称为 Bad habits to kick : choosing the wrong data type

以下是您处理此类事情的方法: (TL;DR;Rextester 上的现场演示)

首先,创建并填充示例表(在您以后的问题中保存我们这一步):

CREATE TABLE MyTable (
    Id int identity(1,1),
    UpdatedDate nvarchar(30)
);


INSERT INTO MyTable(UpdatedDate) VALUES
('01-06-2020 12:00:00 AM'),
('6-17-2020 12:00:00 AM'),
('02-30-2020 12:00:00 AM'), -- Bad data
('1-06-2020 12:00:00 ZM'),  -- Bad data
('01-06-2020 13:00:00 PM'),
('13-06-2020 13:00:00 PM'),  -- Bad data
('01-06-2020 1:00:00 PM'),
('1-6-2020 1:00:00 AM'),
('Not a date at all...');  -- Bad data

请注意,我添加了一些包含无法转换为 DateTime 值的数据的行。

然后,第一步是获取转换失败的值列表。

SELECT Id, UpdatedDate
FROM dbo.MyTable
WHERE TRY_CONVERT(DateTime2, UpdatedDate, 110) IS NULL;

这将返回以下记录集(对于此示例数据):

Id  UpdatedDate
3   02-30-2020 12:00:00 AM
4   1-06-2020 12:00:00 ZM
6   13-06-2020 13:00:00 PM
9   Not a date at all...

现在您有了列表,您可以决定是否要手动修复它们,删除记录, 或者干脆忽略这些值,将新的UpdatedDateNULL 留在这些行中。

完成后,向表中添加一个具有临时名称的新列。 我建议使用DateTime2 而不是DateTime - it's just a better data type.

ALTER TABLE dbo.MyTable 
ADD UpdatedDate_New DateTime2 NULL;

接下来,您填充此列(我假设您已决定将其保留为 null,因为原始列的数据无法转换为 DateTime2):

UPDATE dbo.MyTable
SET  UpdatedDate_New = TRY_CONVERT(DateTime2, UpdatedDate, 110);

现在,从表中删除旧列:

ALTER TABLE dbo.MyTable 
DROP COLUMN UpdatedDate;

最后,重命名新列:

EXEC sp_Rename 'dbo.MyTable.UpdatedDate_New', 'UpdatedDate', 'COLUMN';

验证结果:

SELECT *
FROM dbo.MyTable

结果:

Id  UpdatedDate
1   06.01.2020 00:00:00
2   17.06.2020 00:00:00
3   NULL
4   NULL
5   06.01.2020 13:00:00
6   NULL
7   06.01.2020 13:00:00
8   06.01.2020 01:00:00
9   NULL

最后一个问题我没有在这个答案中解决,那就是原始列的依赖关系。您应该检查什么取决于原始列,因为有时您必须根据更改的列对代码进行调整。

【讨论】:

【解决方案2】:

这取决于您对dateformat 的设置:

create table t(dt varchar(10));

insert t(dt) select '20/7/2020';

set dateformat mdy;

alter table t alter column dt datetime ;
--error

set dateformat dmy;

alter table t alter column dt datetime;
-- ok, because the format of the dates in the table is dmy

在您的情况下,您的值的格式为月/日/年。所以需要执行set dateformat mdy

【讨论】:

  • 这是假设所有值实际上都是有效的。使用 nvarchar 数据类型很难确保这一点。是什么阻止用户输入13-35-1000 Ba:na:na AM
  • 没什么,但是你显然不能将列转换为日期时间,你必须先做一些数据清理。
  • 因此在我的回答中使用Try_convert,以及识别不良数据的第一步......
  • 当然。但是这个问题是关于如何从一个转换到另一个,而不是关于如何进行数据清理。我会提取包含无效日期的数据,将其提供给企业,进行更正,然后转换列。我们不能简单地假设我们可以丢弃数据。
  • 如果你仔细阅读,我写过基本上有两种选择:要么手动修复损坏的数据,要么忽略并转储它(整行或不使用)。诚然,更好的选择是修复损坏的数据,但这并不总是一种选择(想象一个只有 50 万条记录的小表,其中 1% 的更新日期值不正确。这是 5,000 行手动啜饮。不是真的现在是一个不错的选择,是吗?
【解决方案3】:

您必须使用月/日/年格式。如下例所示:

IF OBJECT_ID('TABLE1') IS NOT NULL
    DROP TABLE TABLE1
GO
CREATE TABLE TABLE1 (myDate NVARCHAR(128))
GO
INSERT TABLE1 VALUES('01-06-2020 12:00:00 AM'), ('6-17-2020 12:00:00 AM')
GO
SET DATEFORMAT MDY;
GO
ALTER TABLE TABLE1 ALTER COLUMN myDate DATETIME
GO
SELECT * FROM TABLE1

在这个数据集中,它可以正常工作。

【讨论】:

  • 这是假设所有值实际上都是有效的。使用nvarchar 数据类型很难确保这一点。是什么阻止用户输入13-35-1000 Ba:na:na AM
猜你喜欢
  • 1970-01-01
  • 2012-07-23
  • 2015-03-11
  • 2015-02-13
  • 1970-01-01
  • 2016-10-22
  • 2013-08-19
  • 1970-01-01
  • 2017-03-31
相关资源
最近更新 更多