快速、简单、懒惰的方式,让您继续使用错误的数据类型并允许各种垃圾进入您的表
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/2013
7 月 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