1

我有一个表格,其中包含几个字段,它们以格式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

有人可以帮我吗?

4

3 回答 3

13

快速、简单、懒惰的方式,让您继续使用错误的数据类型并允许各种垃圾进入您的表

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;

现在,在这种情况下,它将返回NULL任何不包含有效日期d/m/y格式的列值。如果要排除这些行而不是将它们包含在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格式有效的日期并不一定意味着它是用户想要的。如果您在07/04/20137 月 4 日有一个美国人输入,您会错误地将其视为 4 月 7 日。这就是为什么区域格式喜欢d/m/y并且m/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');

结果:

Msg 242, Level 16, State 3, Line 2
La conversion d'un type de données varchar en type de données datetime a créé une valeur hors limites。


一些背景

请阅读这篇文章:

最后一点

请停止创建包含空格的列名或别名。如果您需要视觉分离,请使用下划线。这些都是列名的更好选择,不要以任何方式改变含义,并且不需要在每个引用周围都使用难看的方括号:

Start_Date
StartDate
于 2013-03-12T13:33:22.283 回答
1

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

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

此外,如果您在转换为日期类型之前获取日期会更好。如果它们都是格式,您可以将它们放在( ) 中,如下所示。none culture specific ISO formatdd/mm/yyyyISO format'yyyymmdd'

SQL 小提琴演示

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]
于 2013-03-12T10:54:55.180 回答
-4

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

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

前任:

SELECT TOP 100 CONVERT(datetime,[Start Date],103)
FROM [YellowCard_NewDesign].[dbo].[vw_All_Requests]
于 2013-03-12T10:23:11.633 回答