简体   繁体   English

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

[英]Convert nvarchar(MAX) to datetime

I have a table with couple of fields nvarchar(MAX) which occupies date in dd/mm/yyyy format. 我有一个包含几个字段nvarchar(MAX)的表,它以dd/mm/yyyy格式占用日期。

I run this query: 我运行此查询:

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

And I get error: 我得到错误:

The conversion of a nvarchar data type to a datetime data type resulted in an out-of-range value. 从nvarchar数据类型到datetime数据类型的转换导致值超出范围。

The weird thing is that when I run this query like: 奇怪的是,当我运行以下查询时:

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

It works but I don't want the TOP X . 它可以工作,但我不希望使用TOP X

My table contains only 36 records. 我的表仅包含36条记录。 so i don't think that there is bad data. 所以我认为没有不良数据。

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

Can someone help me with that? 有人可以帮我吗?

The quick, easy, lazy way that will let you keep using a bad data type and allow all kinds of garbage into your table 快速,轻松,轻松的方式将使您能够继续使用错误的数据类型,并允许各种垃圾进入表

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;

Now, in this case it will return NULL for any column value where it does not contain a valid date in d/m/y format. 现在,在这种情况下,对于不包含d/m/y格式的有效日期的任何列值,它将返回NULL If you want to exclude those rows instead of including them with NULL values, you can add a WHERE clause: 如果要排除这些行而不是使用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;

The reason you can't do this... 您无法执行此操作的原因...

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

...is because SQL Server might attempt the CONVERT before the filter, resulting in the same error you're getting now. ...是因为SQL Server可能会在过滤器之前尝试进行CONVERT ,从而导致与您现在遇到的错误相同的错误。 The CASE expression (in most cases) allows you to control that evaluation order. CASE表达式(在大多数情况下)使您可以控制该评估顺序。

Now, of course, a date that is valid as d/m/y format does not necessarily mean it was what the user intended. 现在,当然,以d/m/y格式有效的日期并不一定意味着它就是用户想要的。 If you had an American enter 07/04/2013 for July 4th, you're going to incorrectly perceive that as April 7th. 如果您有一个美国人在7月4日输入07/04/2013 ,那么您会错误地认为这是4月7日。 This is why regional formats like d/m/y and m/d/y are no good. 这就是为什么像d/m/ym/d/y这样的区域格式不好的原因。


The quick, easy, lazy way that will let you keep using a bad data type but prevent more garbage from getting into your table with a little bit more work 一种快速,轻松,轻松的方式,使您可以继续使用错误的数据类型, 但又需要进行更多的工作,从而防止更多的垃圾进入表中

You should still follow the advice above and fix or remove any data that doesn't conform, and at the very least add a check constraint so that no more junk gets into your table: 您仍应遵循上述建议,并修复删除所有不符合要求的数据,并至少添加一个检查约束,以使表中不再有垃圾:

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

So the following inserts will fail or succeed: 因此,以下插入将失败或成功:

-- 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';

The failures prevent the bad inserts altogether, and only allow valid d/m/y strings into the table. 这些故障可完全防止错误插入,并且仅允许将有效的d/m/y字符串插入表中。 The error message is: 错误消息是:

Msg 241, Level 16, State 1, Line 1 消息241,级别16,状态1,第1行
Conversion failed when converting date and/or time from character string. 从字符串转换日期和/或时间时转换失败。


The right way 正确的方式

First, identify the bad data: 首先,确定不良数据:

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

Now, fix that data, wherever it comes from, so that you can fix the data type. 现在,修复该数据,无论它来自何处,以便您可以修复数据类型。 This might mean a combination of these types of statements: 这可能意味着这些类型的语句的组合:

-- 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;

Then add a DATE column to the source table: 然后将DATE列添加到源表中:

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

Then update the data in that column: 然后更新该列中的数据:

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

Now drop the original column (you may need to adjust indexes that include this column or constraints that reference it): 现在删除原始列(您可能需要调整包括该列的索引或引用该列的约束):

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

Now you can either change the new column name: 现在,您可以更改新的列名:

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

Or change the view: 或更改视图:

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

Or change your application to use the new, better column name. 或更改您的应用程序以使用新的更好的列名。 Don't forget to change all of your data type parameters to use the proper data type too, and when passing in string literals, STOP using regional formats like d/m/y . 不要忘记更改所有数据类型参数以也使用正确的数据类型,并且在传递字符串文字时,请停止使用诸如d/m/y类的区域格式。 Like @Kaf, I prefer yyyymmdd , which can never be misinterpreted, but yyyy-mm-dd will always work for DATE - just not DATETIME , example: 像@Kaf,我宁愿yyyymmdd ,可从来没有被误解,但yyyy-mm-dd会一直为工作DATE -只是不DATETIME ,例如:

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

Result: 结果:

Msg 242, Level 16, State 3, Line 2 消息242,级别16,状态3,第2行
La conversion d'un type de données varchar en type de données datetime a créé une valeur hors limites. 转换日期类型为不合法的时间限制。


Some background 一些背景

Please read this post: 请阅读这篇文章:

As a final note 最后一点

Please stop creating column names or aliases with spaces in them. 请停止创建带有空格的列名或别名。 If you need visual separation, use an underscore. 如果需要视觉分隔,请使用下划线。 These are both much better choices for column names, don't change the meaning in any way, and don't require ugly square brackets around every single reference: 这些都是用于列名的更好的选择,不要以任何方式改变含义,也不需要在每个引用旁边加上丑陋的方括号:

Start_Date
StartDate

Saving Date in string type fields is not a good idea. 在字符串类型字段中保存日期不是一个好主意。 But if that something you have to deal with at the moment, you could run this query with isdate() function to check any bad data and fix them: 但是,如果您现在必须处理这些问题,则可以使用isdate()函数运行此查询以检查任何不良数据并进行修复:

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

Also, it would be better if you get the date in none culture specific ISO format before converting into Date type. 同样,如果在转换为日期类型之前获得非none culture specific ISO format的日期会更好。 If they are all in dd/mm/yyyy format, you could get them in ISO format ( 'yyyymmdd' ) as below. 如果它们都是dd/mm/yyyy格式,则可以按照下面的ISO format'yyyymmdd' )获得它们。

SQL FIDDLE DEMO SQL FIDDLE DEMO

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

So your actual query will be like: 因此,您的实际查询将类似于:

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

This has nothing to do with TOP 40. It means you dont have problematic data in top 40 rows. 这与TOP 40无关。这意味着您在前40行中没有问题数据。

If you have less data in your table, you can simply increase the limit to find out where the error is occuring 如果表中的数据较少,则可以简单地增加限制以找出发生错误的位置

Ex: 例如:

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