【问题标题】:Best way to create new DateTime column from VARCHAR column从 VARCHAR 列创建新 DateTime 列的最佳方法
【发布时间】:2022-01-04 00:43:58
【问题描述】:

我有一个有 1M 行和一个 CREATED_AT varchar 列的表。本列日期字符串的格式:

  • 2021-10-13 05:03:42.638+00
  • 2021-10-18 21:28:49.98+00
  • 2021-12-08 02:09:03.17+00

我想将此字符串转换为新的datetime 列(称为CREATED_DT)。

尝试

select 
    cast([CREATED_AT] as datetime) 
from
    [mydb].[dbo].[mytable]

错误:

从字符串转换日期和/或时间时转换失败

select 
    convert(datetime, [CREATED_AT]) 
from 
    [mydb].[dbo].[mytable]

从字符串转换日期和/或时间时转换失败

select 
    try_convert(DateTime, [CREATED_AT]) 
from
    [mydb].[dbo].[mytable]

查询执行,但返回所有空值。

select 
    try_convert(DateTime, [CREATED_AT], 108) 
from
    [mydb].[dbo].[mytable]

查询执行,但返回所有空值。

select 
    try_cast([CREATED_AT] as datetime) 
from
    [mydb].[dbo].[mytable]

查询执行,但返回所有空值。

【问题讨论】:

  • 使用TRY_CONVERT 和样式代码,但实际上你应该修复你的设计;永远不要将日期和时间值存储为 varchar
  • 我不是这个独立系统/组织的数据所有者。将调查 TRY_CONVERT,对我来说是新的。谢谢
  • 你能定义“最佳”吗?您能否确保包括所有可能的边缘情况(例如,只有 1 或 0 个小数位,或者缺少 +00,或者如果需要观察 +00 以外的任何偏移量),并指出丢失精度是否正常(.例如,638 永远不能存储为日期时间)。除非您推回此系统/组织,以便为使用它们的系统提供正确、明确的日期格式,否则可能存在各种垃圾数据。

标签: sql datetime casting sql-server-2012


【解决方案1】:

假设你永远不必关心最后无用的+00(要么它总是+00,要么你不关心它是否是别的东西),你可以失去一点点精确度,您可以取剩下的 22 个字符并尝试使用安全样式编号(在您的情况下为 120)转换值:

DECLARE @d table(CREATED_AT varchar(32));

INSERT @d(CREATED_AT) VALUES
('2021-10-13 05:03:42.638+00'),
('2021-10-18 21:28:49.98+00'),
('2021-12-08 02:09:03.17+00');

SELECT CREATED_AT, 
  as_datetime = TRY_CONVERT(datetime, LEFT(CREATED_AT, 22), 120) 
FROM @d;

如果您不想丢失精度(例如,您不能将 .638 保留为 datetime),或者某些值可能有 1 位或 0 位小数,或者某些值可能没有完全包含+xx,你可以做一些类似的事情,但使用一些字符串技巧来截断+出现的值(如果没有,也可以处理):

SELECT CREATED_AT,
  as_datetime2 = TRY_CONVERT(datetime2(3), 
    LEFT(CREATED_AT, COALESCE(NULLIF(CHARINDEX('+', 
    CREATED_AT), 0), 32)-1), 120) 
FROM @d;

为了说明为什么在这里使用 120 样式编号很重要,请参阅 this db<>fiddle

【讨论】:

  • 这很有魅力,感谢您提供的 dbfiddle。从中学到了一些东西。非常感激。 2022 年快乐!
【解决方案2】:

您正在寻找的样式是127,但您的问题是您只包括时区的小时,您还需要分钟。因此,只需将:00 添加到每个值即可。

然后,您可以使用 SWITCHOFFSET 从那里转换为常规 datetime

DECLARE @v varchar(30) = '2021-10-13 05:03:42.638+00';

SELECT TRY_CONVERT(datetimeoffset, @v + ':00', 127);

SELECT CONVERT(datetime, SWITCHOFFSET(TRY_CONVERT(datetimeoffset, @v + ':00', 127), 0));

db<>fiddle

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-11
    相关资源
    最近更新 更多