处理日期和时间数据--字符串文字
当需要指定日期和时间数据类型的字符串文字值(常量)时,应该考虑以下几件事。首先,SQL Server并没有提供表达日期和时间字符串的具体方法,这听起来可能会有点古怪;相反,它允许你指定不同类型的字符串文字,再将其转换(显示或隐式地)为相应的日期和时间数据类型。使用字符串来表示日期和时间值是一种最佳实践,如下面这个例子所示:
2 FROM Sales.Orders
3 WHERE orderdate ='20070212';
SQL Server会把文字'20070212'看作是一个字符串常量,而不是日期和时间常量;但因为表达式涉及了两种不同类型的操作数,所以就要把其中一种操作数的类型转换为另一种。当一个表达式涉及的两种操作数具有不同类型时,就需要把其中一种操作数的类型隐式地转换为另一种操作数的类型。通常,类型之间的隐式转换会基于所谓的数据类型优先级(datatype precedence)原则。SQL Server预先定义了数据类型之间的优先关系,运算时通常是将具有较低数据类型优先级的操作数隐式地转换为具有较高优先级的类型。在这个例子中,是把字符串文字转换成了列具有的数据类型(DATETIME),因为SQL Server认为字符串的数据类型优先级比日期和时间数据类型的要低。隐式转换规则也不总是这么简单,其实在过滤条件和其他表达式中应用的规则是不同的。不过,出于我们讨论的目的,现在尽可能先让事情简单些。有关数据类型优先级的完整列表,请参阅SQL Server联机丛书的“数据类型优先级”一节。
这里须要澄清的一点是,在前面的例子中,隐式转换是在幕后进行的。前面的查询在逻辑上等于以下查询(显示地将字符串转换为DATETIME数据类型):
2 FROM Sales.Orders
3 WHERE orderdate =CAST('20070212'ASDATETIME);
要重点注意一些日期和时间常量字符串是与语言相关的,也就是说当把它们转换成日期和时间类型时,根据当前会话中有效的语言设置,SQL Server对它们解释的方法也可能会有所不同。DBA定义的每个登录都有一个与之关联的默认语言,除非显示地修改,这一语言将一直是会话中的有效语言。可以使用SET LANGUAGE命令改写会话中的默认语言,但一般不推荐这么做,因为代码中的某些地方可能会依赖用户的默认语言。
会话中的有效语言将会在后台影响几个与语言相关的设置,其中有一个称为DATEFORMAT的设置,它会决定当把字符串类型转换成日期和时间类型时,SQL Server如何解释输入的字符串常量。DATEFORMAT设置是由字符d、m,以及y的组合来表示的。例如,us_english语言设计会把DATEFORMAT设置为mdy,而British语言设置则将DATEFORMAT设置为dmy。可以用SET DATEFORMAT命令改写会话中的DATEFORMAT设置,但如前面所述,通常不推荐对语言相关的设置进行修改。例如,考虑字符串文字'02/12/2007'。当把这个字符串文字转换成DATETIME、DATE、DATETIME2或DATETIMEOFFSET这些类型时,SQL Server可以将数据解释为2007年2月12日,也可以解释为2007年12月2日。这时,有效的LANGUAGE/DATEFORMAT设置就是决定因素。为了演示对相同字符串文字的不同解释转换,请运行以下代码:
2 SELECTCAST('02/12/2007'ASDATETIME);
3
4 SET LANGUAGE us_english;
5 SELECTCAST('02/12/2007'ASDATETIME);
注意它们的输出,在两种不同的语言环境中,字符串文字的解释也各不相同:
注意,LANGUAGE/DATEFORMAT只会影响输入值的解释方式,对数据输出中使用的格式没有任何影响。输出格式是由客户端工具(如OLEBD)使用的数据库接口决定的,而不是由LANGUAGE/DATEFORMAT设置决定的。例如,OLEDB和ODBC都是按照'YYYY-MM-DD hh:mm:ss.nnn'的格式来表示DATETIME值。
因为你写的代码可能最终要交付给国际用户使用,而他们登录用的语言设置也各不相同;所以,理解一些日期和时间的字符串文字是语言独立的,就显得尤为重要。强烈建议按照语言无关的方式来编写日期和时间字符串文字。对于语言无关的格式,SQL Server总能以相同的方式加以解释,而不受语言相关设置的影响。下表列出了每种日期和时间类型的语言无关的字符串文字格式。
数据类型 |
语言中立的格式 |
推荐格式及示例 |
DATETIME |
YYYYMMDD hh:mm:ss.nnn YYYY-MM-DDThh:mm:ss.nnn YYYYMMDD |
20090212 12:30:15.123 2009-02-12T12:30:15.123 20090212 |
SMALLDATETIME |
YYYYMMDD hh:mm YYYY-MM-DDThh:mm YYYYMMDD |
20090212 12:30 2009-02-12T12:30 20090212 |
DATE |
YYYYMMDD YYYY-MM-DD |
20090212 2009-02-12 |
DATETIME2 |
YYYYMMDD hh:mm:ss.nnnnnnn YYYY-MM-DD hh:mm:ss.nnnnnnn YYYY-MM-DDThh:mm:ss.nnnnnnn YYYYMMDD YYYY-MM-DD |
20090212 12:30:15.1234567 2009-02-12 12:30:15.1234567 2009-02-12T12:30:15.1234567 20090212 2009-02-12 |
DATETIMEOFFSET |
YYYYMMDD hh:mm:ss.nnnnnnn [+|-]hh:mm YYYY-MM-DD hh:mm:ss.nnnnnnn [+|-]hh:mm YYYYMMDD YYYY-MM-DD |
20090212 12:30:15.1234567 +02:00 2009-02-12 12:30:15.1234567 +02:00 20090212 2009-02-12 |
TIME |
hh:mm:ss.nnnnnnn |
12:30:15.1234567 |
上表中有两点要注意。对于所有包括日期和时间组成部分的类型,如果不在字符串文字中指定时间,则SQL Server默认将时间设置为午夜。如果不指定时区,SQL Server将采用00:00。此外,也要重点注意'YYYY-MM-DD'和'YYYY-MM-DD hh:mm...'格式,当转换到DATETIME或SMALLDATETIME类型时,它们是语言相关的;当转换到DATE、DATETIME2,以及DATETIMEOFFSET时,它们是语言无关的。
例如,注意在以下代码中,语言设置对于以'YYYYMMDD'格式表示的字符串文字如何转换为DATETIME类型的数据并没有影响:
2 SELECTCAST('20070212'ASDATETIME);
3
4 SET LANGUAGE us_english;
5 SELECTCAST('20070212'ASDATETIME);
输出结果表明,两种情况下的字符串文字都被转换成2007年2月12日的日期:
优先使用类似'YYYYMMDD'这样语言无关的格式是一条最佳实践原则,再怎么强调这一点也不为过。因为无论LANGUAGE/DATEFORMAT如何设置,SQL Server对这些格式的解释都是相同的。
如果你坚持要用与语言相关的格式来表示日期和时间字符串文字,则可以使用CONVERT函数,在它的第3个参数中指定一个表示正在使用的样式的数字,显示地将字符串文字转换成想要的数据类型。SQL Server联机文档在“CAST和CONVERT函数”一节用一个表格列举出了所有的样式数字和它们各种代表的格式。例如,如果想指定字符串文字'02/12/2007'的格式为mm/dd/yyyy,则可以使用样式号101,如下所示:
这样,不论当前会话有效的语言设置是什么,SQL Server都会把该字符串文字解释为2007年2月12日。
如果想采用dd/mm/yyyy的格式,可以选用样式号103:
这次字符串文字将被解释为2007年12月2日。