第一个问题是您将日期存储在字符串列中。你为什么不使用DATE
or DATETIME
?如果您这样做是为了保留 d/m/y 格式,请停止这样做。格式是表示层的责任,不应影响存储。另外,你为什么用NVARCHAR
?您预计在需要 Unicode 的日期文字中会出现什么样的符号?
接下来是您没有适当的验证,并且错误的数据最终出现在您的列中。您可以按如下方式识别此不良数据:
SET DATEFORMAT DMY;
SELECT * FROM dbo.table
WHERE ISDATE(postdate) = 0;
现在,修复该数据,方法是在您知道它是什么时更新它,或者将这些行设置为NULL
,直到查询返回 0 行。
请注意,您可能有像 11/13/2013 这样的数据,这显然不在 D/M/Y 格式中,如果是这种情况,您也可能拥有看似有效但输入 06/09 的用户的数据/2013 是指 6 月 9 日还是 9 月 6 日?你怎么能确定?
防止明天再次发生此问题的最安全方法可能是添加新列,移动数据,删除旧列,并在所有层停止使用字符串。
ALTER TABLE dbo.table
ADD d DATE;
UPDATE dbo.table
SET d = CONVERT(DATETIME, postdate, 103);
ALTER TABLE dbo.table
DROP COLUMN postdate;
EXEC sp_rename N'dbo.table.d', N'postdate', N'COLUMN';
您可以在不进行所有这些交换的情况下内联执行此操作,如下所示:
SET DATEFORMAT DMY; -- the key you missed in your attempt I think
ALTER TABLE dbo.table ALTER COLUMN postdate DATE;
如果您无法修复该列本身,那么至少考虑在该列周围添加一个检查约束和一些验证,这样明天就不会出现错误数据。例如:
ALTER TABLE dbo.table
ADD CONSTRAINT prevent_bad_dates
CHECK (CONVERT(DATE, postdate, 103) >= '19000101');
假设您在执行插入的存储过程中使用 varchar 参数,甚至在插入之前进行验证:
ALTER PROCEDURE dbo.whatever
@postdate NVARCHAR(10), -- assuming here
@other_params...
AS
BEGIN
SET NOCOUNT ON;
SET DATEFORMAT DMY;
IF ISDATE(@postdate) = 0
BEGIN
RAISERROR('Incorrect format for date: %s.', 11, 1, @postdate);
RETURN;
END
... perform insert...
END
这仍然无法帮助您理解输入的用户是06/09/2013
指 6 月 9 日还是 9 月 6 日。因此,我敦促您从用户那里拿走这种徒手的临时条目,并强制他们使用日历控件或日期选择器或其他东西,这样您就可以完全控制格式。如果您需要将字符串文字发送到 SQL Server,最安全的格式将是YYYYMMDD
. 此外,您应该注意使用BETWEEN
而不是开放式范围的潜在问题。请阅读以下帖子: