简介
日期是一种复杂的信息。它表示一年中特定的一天。可以按星期、月份、季度等将日期分组。这种分组便于比较不同年份在一段特定时期的结果。
Informix Dynamic Server(IDS)提供了一些处理日期的功能。本文回顾当前已有的一些函数,并提供一些附加的有用的函数。
|
IDS 日期函数
IDS 包含两种“日期”数据类型。一种是 DATE
,另一种是 DATETIME
。DATE
表示一天,而 DATETIME
表示一个特定的时刻,其精度可以从年到秒。IDS 提供了以下函数来操纵这些类型:
DATE(VARCHAR(10)) 返回 DATE 类型
该函数以一个字符串变量为参数,其格式由环境变量 DBDATE
指定,并返回一个 DATE
类型。US English 地区的缺省格式是“MDY4/”。
DATE(DATETIME) 返回 DATE 类型
这个函数与上一个函数相同,但是其输入参数是可以为任意精度的 DATETIME
。
DATE(INTEGER) 返回 DATE 类型
INTEGER
参数表示从 1899 年 12 月 31 日以来的天数。
DAY(DATE) 返回 INTEGER 类型
DAY
函数返回月中的日,格式为 INTEGER
。
DAY(DATETIME)
与上一个函数相同,只是这个函数的输入参数为任意精度的 DATETIME
。
EXTEND(DATE, precision) 返回 DATETIME 类型
EXTEND
函数调整 DATE
参数的精度,并返回适当的 DATETIME
。由于说起来有点儿模糊,这里举一个例子: EXTEND(DATE(1), YEAR TO SECOND)
EXTEND(DATETIME, precision) 返回 DATETIME 类型
与上一个函数相同,但操作的对象是一个 DATETIME
,而不是一个 DATE
。
MONTH(DATE) 返回 INTEGER 类型
MONTH
从参数 DATE
中提取出月份。
MONTH(DATETIME) 返回 INTEGER 类型
该函数从任意精度的 DATETIME
中提取出月份。
WEEKDAY(DATE) 返回 INTEGER 类型
WEEKDAY
函数根据指定的 DATE
返回一个 INTEGER
,表示星期几。0 表示星期天,6 表示星期六。
WEEKDAY(DATETIME) 返回 INTEGER 类型
与上一个函数相同,但操作对象是 DATETIME
。
YEAR(DATE) 返回 INTEGER 类型
该函数从指定的参数 DATE
中提取出年份。
YEAR(DATETIME) 返回 INTEGER 类型
与上一个函数相同,但操作对象是 DATETIME
。
MDY(INTEGER, INTEGER, INTEGER) 返回 DATE 类型
该函数根据三个 INTEGER
参数创建一个 DATE
。这些参数分别指定月、日和年。注意,年是四位的整数。
TO_CHAR(DATE, VARCHAR(??)) 返回 VARCHAR(??) 类型
该函数带一个 DATE
参数和一个格式参数,并返回一个表示日期的字符串,该字符串遵从要求的格式。格式字符串可以包括:
TO_CHAR(DATE, VARCHAR(??)) 返回 VARCHAR(??) 类型
同上。
TO_DATE(VARCHAR(??), VARCHAR(??)) 返回 DATE 类型
TO_CHAR
的逆向操作,使用相同的格式字符串作为第二个参数。 除了上述函数外,还有两个对日期的处理有影响的环境变量:
DBDATE
:提供日期的终端用户格式。在 SQL 参考手册(第 3-25 页)中对此有描述。 DBCENTURY
:定义当输入两位数而不是 4 位数的日期时,如何将年份展开。可以接受的值有 R、P、F 和 C。它们分别表示 Current、Previous、Future 和 Closest。如果没有设置 DBCENTURY
,则 R 为缺省值。在 SQL 参考手册(关于 IDS 10.0 的第 3-22 页)中描述了 DBCENTURY
。 最后,IDS 定义了两个内建的函数,用于返回当前日期值。CURRENT
返回一个 DATETIME
值,而 TODAY
则返回当天的日期。
|
使用日期函数
前面描述的那些函数提供了输入、输出、格式化和提取信息的功能。我想讨论的第一种有趣的用法是基于字符串的日期的输入。
函数 DATE()
接收一个字符串作为输入,但是根据 DBDATE
和 DBCENTURY
的设置对它进行不同的处理。首先来看 DBCENTURY
。
DBCENTURY
的缺省值为 R。这意味着世纪由当前日期的世纪决定。下面的例子假设在缺省的 US English 地区运行:
|
如果 DBCENTURY
被设为 P,则所指的世纪是离当前日期最近的一个世纪。按照这种设置,前面的例子变成:
|
DBDATE
环境变量为日期转换提供了另一种可能的变化。对于 US English 地区,它的缺省值为“MDY4/”。这意味着日期字符串的各部分之间以“/”隔开,它们之间的顺序是月、日和年。注意,预期的年是 4 位数,但是也可以根据 DBCENTURY
设置的规则进行补足。可以修改 DBDATE
的值,以使用国际日期格式。则 DBDATE
值将是“Y4MD-”。除了对字符串类型的输入日期有影响外,这个值还影响日期到字符串的转换:
|
要更详细地显示日期,可以使用 TO_CHAR
函数,并像上一节中描述的那样提供一个格式:
|
可以使用一些已有的函数来提取诸如月、日之类的值,并在用表达式定义表分段时使用那些值。也可以在 SQL 语句中将它们用于分组。例如,如果您想了解每月有多少订单,那么可以使用以下语句:
|
这种类型的分组在各种报告中都很有用。如果愿意利用 IDS 的一些基本的可扩展特性,还可以做更多的事情。
|
IDS 可扩展性
IDS 是最早扩展数据库的功能以适合用户环境的数据库。从 1997 年 IDS version 9.01 开始,可扩展性就可用了,而在 IDS version 10 中继续提供了可扩展性,并作了改进。可以创建新的数据类型、新的函数,甚至新的聚集。函数和聚集可以用 C、Java 或 SPL 编写。如果您想了解更多关于可扩展性的用途的信息,请参阅本文后面提供的参考资料。
我通常用 C 编写用户定义函数。但是对于本文,我用 SPL 编写用户定义函数。SPL 的优点在于它是 IDS 用户熟知的语言。在编写存储过程时同样也使用了这种语言。
|
函数索引
IDS V9.x 和更高版本支持函数索引(functional index)的概念。这意味着可以在函数的结果上创建索引。然后,可以使用索引来加快对 SQL 语句中包含函数的查询的处理。
内建函数是在 IDS 添加可扩展特性之前创建的。不能直接在内建函数的结果上创建索引。不过,可以将内建函数包装在一个 SPL 函数里面。例如,如果想在月上创建一个索引,那么可以创建一个 SPL 函数,如下所示:
|
有了这个包装器函数,便可以创建一个索引:
|
然后,可以使用利用了上述索引的 SQL 语句,比如:
|
|
新的日期函数
可以从日期中提取出更多信息:年中周、月中周和季度。
首先来看年中日函数。有了这样的函数,就可以按周报告活动,而不必为每个报告编写特定的存储过程,或编写定制的应用程序代码。可以使用 IDS 中内建的函数来构造这个函数。这样,这个函数看上去就非常简单了:
|
实现该函数的关键之处在于一个日期实际上就是一个整数,这个整数表示从 1899 年 12 月 31 日以来的天数。这意味着如果得到 1 月 1 日的日期,那么就只需做一个简单的减法。
可以将这个函数用在 EXECUTE FUNCTION
语句中,或者用于在一个函数或存储过程中设置一个值,或者用在 SQL 语句中。
|
年中周函数要稍微复杂一些。它采用类似的计算,不过需要除以每周 7 天:
|
该函数的关键在于理解 WEEKDAY 内建函数提供的偏移量。WEEKDAY 函数为星期天返回 0,一直到为星期六返回 6。如果 1 月 1 日是星期天,那么我们知道 1 月 8 日是第二周的星期天。如果 1 月 1 日从另一个周中日开始,则意味着第一周更短一些。WEEKDAY 内建函数为我们提供了偏移量,以便计算出一个日期属于一年中的第几周。
week_of_year() 函数在一年中的最后一个星期和下一年的第一个星期上存在问题。例如,2004 年 12 月 31 日是星期五,2005 年 1 月 1 日是星期六。而 week_of_year 函数会提供以下结果:
|
这种行为正确吗?这要由您来决定。如果不正确,那么需要根据您的需求修改代码,以便解决这个问题。为此只需在 SPL 函数中添加几行代码。
如果想计算日期在一个月中属于第几周,那么可以使用相同的函数,不过不是以 1 月 1 日作为起始日期,而是以参数指定的日期当月的第 1 天作为起始日期:
|
|
quarter() 函数
有些数据库产品提供了 quarter() 函数。它通常返回一个 1 到 4 之间的数字。提供 quarter() 函数的问题是,它假设一个特定的日历:标准历年。
很多公司需要根据他们自己的业务年来计算季度,而业务年往往与标准历年不一致。甚至有些组织必须根据需要做的事情来计算季度。例如,一些学校必须计算日历季度、学年季度和业务季度。
我们首先来看历年季度的一个简单的实现:
|
在这个实现中,我将年也放在季度的表示中。例如,2005 年第 3 季度被表示为 200503。当 SQL 语句跨越多个年度时,这样做可以简化处理。也可以创建不同的实现,例如返回一个字符串,而不是返回整数。您必须根据自己的需求做决定。
如前所述,可能需要根据不是 1 月 1 日的起始日期来计算季度。在历年不同于季度年的情况下,这增加了一定的复杂性。例如,假设一家公司从 9 月 1 日开始它的会计年度。这意味着 2005 年 9 月 1 日实际上是 2006 年第一季度的开始,12 月 1 日是第二季度的开始,依此类推。
下面的代码展示了从 9 月 1 日开始新一年的实现。该代码很容易根据不同的起始日期进行调整:
|
与 quarter() 函数相比,该函数中增加的处理是将当前月向前移动了几个月,以便计算与业务年匹配的季度。
|
使用新函数
有了这些函数之后,可以将它们用在 SQL 语句中,就像它们是在 IDS 中内建的函数一样。例如:
|
而且可以在这些函数上创建索引:
|
这样便可以灵活地让数据库返回您想要的信息。IDS 可扩展性在很多其他领域也很有用。请参阅“参考资料”小节,以获得其他关于该主题的文章。