繁体   English   中英

将nvarchar(MAX)转换为日期时间

[英]Convert nvarchar(MAX) to datetime

我有一个包含几个字段nvarchar(MAX)的表,它以dd/mm/yyyy格式占用日期。

我运行此查询:

SELECT CONVERT(datetime,[Start Date],103)
FROM [YellowCard_NewDesign].[dbo].[vw_All_Requests]

我得到错误:

从nvarchar数据类型到datetime数据类型的转换导致值超出范围。

奇怪的是,当我运行以下查询时:

SELECT TOP 40 CONVERT(datetime,[Start Date],103)
FROM [YellowCard_NewDesign].[dbo].[vw_All_Requests]

它可以工作,但我不希望使用TOP X

我的表仅包含36条记录。 所以我认为没有不良数据。

20/03/2013
20/03/2013
10/03/2013
10/03/2013
11/03/2013
06/03/2013
06/03/2013
21/03/2013
12/03/2013
03/03/2013
18/03/2013
04/03/2013
28/02/2013
28/02/2013
28/02/2013
28/02/2013
31/01/2013
15/01/2013
23/01/2013
23/01/2013
31/01/2013
23/01/2013
30/01/2013
31/01/2013
24/01/2013
24/01/2013
24/01/2013
24/01/2013
24/01/2013
30/01/2013
23/01/2013
22/01/2013
23/01/2013
23/01/2013
23/01/2013
23/01/2013

有人可以帮我吗?

快速,轻松,轻松的方式将使您能够继续使用错误的数据类型,并允许各种垃圾进入表

SET DATEFORMAT DMY;

SELECT 
  -- other columns, 
  [Start Date] = CONVERT(DATETIME, CASE WHEN ISDATE([Start Date]) = 1
  THEN [Start Date] END, 103)
FROM 
  dbo.vw_All_Requests;

现在,在这种情况下,对于不包含d/m/y格式的有效日期的任何列值,它将返回NULL 如果要排除这些行而不是使用NULL值包括它们,则可以添加WHERE子句:

SET DATEFORMAT DMY;

SELECT 
  -- other columns, 
  [Start Date] = CONVERT(DATETIME, CASE WHEN ISDATE([Start Date]) = 1
  THEN [Start Date] END, 103)
FROM 
  dbo.vw_All_Requests
WHERE ISDATE([Start Date]) = 1;

您无法执行此操作的原因...

SELECT CONVERT(DATETIME, [Start Date], 103)
FROM ...
WHERE ISDATE([Start Date]) = 1;

...是因为SQL Server可能会在过滤器之前尝试进行CONVERT ,从而导致与您现在遇到的错误相同的错误。 CASE表达式(在大多数情况下)使您可以控制该评估顺序。

现在,当然,以d/m/y格式有效的日期并不一定意味着它就是用户想要的。 如果您有一个美国人在7月4日输入07/04/2013 ,那么您会错误地认为这是4月7日。 这就是为什么像d/m/ym/d/y这样的区域格式不好的原因。


一种快速,轻松,轻松的方式,使您可以继续使用错误的数据类型, 但又需要进行更多的工作,从而防止更多的垃圾进入表中

您仍应遵循上述建议,并修复删除所有不符合要求的数据,并至少添加一个检查约束,以使表中不再有垃圾:

ALTER TABLE dbo.SourceTable 
  ADD CONSTRAINT CK_SillyMaxColumnIsValidDate
  CHECK (CONVERT(DATE, [Start Date], 103) >= '0001-01-01');

因此,以下插入将失败或成功:

-- these succeed:
INSERT dbo.SourceTable([Start Date]) SELECT '01/01/2005';
INSERT dbo.SourceTable([Start Date]) SELECT '25/01/2005';
GO
-- fails:
INSERT dbo.SourceTable([Start Date]) SELECT '01/25/2005';
GO
-- fails:
INSERT dbo.SourceTable([Start Date]) SELECT 'garbage';

这些故障可完全防止错误插入,并且仅允许将有效的d/m/y字符串插入表中。 错误消息是:

消息241,级别16,状态1,第1行
从字符串转换日期和/或时间时转换失败。


正确的方式

首先,确定不良数据:

SELECT [Start Date] 
  FROM dbo.vw_All_Requests
  WHERE ISDATE([Start Date]) = 0;

现在,修复该数据,无论它来自何处,以便您可以修复数据类型。 这可能意味着这些类型的语句的组合:

-- correct the date for specific bad values
UPDATE dbo.SourceTable 
  SET [Start Date] = '03/12/2012'
  WHERE [Start Date] = 'whatever';

-- remove the value altogether for specific bad values
UPDATE dbo.SourceTable 
  SET [Start Date] = NULL
  WHERE [Start Date] = 'whatever';

-- remove the row altogether for specific bad values
DELETE dbo.SourceTable 
  WHERE [Start Date] = 'whatever';

-- remove all rows with bad values
SET DATEFORMAT DMY;

DELETE dbo.SourceTable
  WHERE ISDATE([Start Date]) = 0;

然后将DATE列添加到源表中:

ALTER TABLE dbo.SourceTable
  ADD StartDate -- no space!
  DATE;

然后更新该列中的数据:

UPDATE dbo.SourceTable
  SET StartDate = CONVERT(DATETIME, [Start Date], 103);

现在删除原始列(您可能需要调整包括该列的索引或引用该列的约束):

ALTER TABLE dbo.SourceTable
  DROP COLUMN [Start Date];

现在,您可以更改新的列名:

EXEC sp_rename N'dbo.SourceTable.StartDate', 'Start Date', 'COLUMN';

或更改视图:

ALTER VIEW dbo.vw_All_Requests
AS
   SELECT 
    ...
    [Start Date] = StartDate

或更改您的应用程序以使用新的更好的列名。 不要忘记更改所有数据类型参数以也使用正确的数据类型,并且在传递字符串文字时,请停止使用诸如d/m/y类的区域格式。 像@Kaf,我宁愿yyyymmdd ,可从来没有被误解,但yyyy-mm-dd会一直为工作DATE -只是不DATETIME ,例如:

SET LANGUAGE FRENCH;
SELECT CONVERT(DATETIME, '2012-03-15');

结果:

消息242,级别16,状态3,第2行
转换日期类型为不合法的时间限制。


一些背景

请阅读这篇文章:

最后一点

请停止创建带有空格的列名或别名。 如果需要视觉分隔,请使用下划线。 这些都是用于列名的更好的选择,不要以任何方式改变含义,也不需要在每个引用旁边加上丑陋的方括号:

Start_Date
StartDate

在字符串类型字段中保存日期不是一个好主意。 但是,如果您现在必须处理这些问题,则可以使用isdate()函数运行此查询以检查任何不良数据并进行修复:

SELECT [Start Date]
FROM [YellowCard_NewDesign].[dbo].[vw_All_Requests]
WHERE isdate([Start Date]) = 0

同样,如果在转换为日期类型之前获得非none culture specific ISO format的日期会更好。 如果它们都是dd/mm/yyyy格式,则可以按照下面的ISO format'yyyymmdd' )获得它们。

SQL FIDDLE DEMO

declare @d varchar(max) = '20/03/2013'
select convert(datetime,right(@d,4) + substring(@d,4,2) + left(@d,2))

因此,您的实际查询将类似于:

SELECT CONVERT(datetime,right([Start Date],4) + 
                               substring([Start Date],4,2) + 
                               left([Start Date],2))
FROM [YellowCard_NewDesign].[dbo].[vw_All_Requests]

这与TOP 40无关。这意味着您在前40行中没有问题数据。

如果表中的数据较少,则可以简单地增加限制以找出发生错误的位置

例如:

SELECT TOP 100 CONVERT(datetime,[Start Date],103)
FROM [YellowCard_NewDesign].[dbo].[vw_All_Requests]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM