使用整数值的日期算术

时间:2010-06-11 01:00:21

标签: sql postgresql query-optimization

问题

字符串连接正在减慢查询速度:

date(extract(YEAR FROM m.taken)||'-1-1') d1,
date(extract(YEAR FROM m.taken)||'-1-31') d2

这在代码中实现为字符串的一部分,后面是p_变量是整数,由最终用户提供输入):

date(extract(YEAR FROM m.taken)||''-'||p_month1||'-'||p_day1||''') d1,
date(extract(YEAR FROM m.taken)||''-'||p_month2||'-'||p_day2||''') d2

查询的这一部分在3.2秒内运行日期,1.5秒没有,这让我相信有充足的改进空间。

查询的总运行时间小于10秒;我希望将整个查询降低到大约2或3秒。硬件升级已经发生。 ; - )

版本

PostgreSQL 8.4.4。

问题

创建日期的更好方法是什么(可能没有连接)?

更新

这看起来很有希望:PGTYPESdate_mdyjul

非常感谢!

3 个答案:

答案 0 :(得分:2)

可悲的是,我认为没有其他方法可以建立一个没有文本连接的日期。

是的,坦率地说,我不喜欢Postgresql在这里有的方法。似乎大多数日期操作必须通过将日期字段提取为整数,将它们作为文本转换,将它们附加到更多文本以创建日期的文本表示,然后告诉postgres将该文本解析为日期来进行...这种气味对我不好,我本能地认为通过解析字符串来建立日期应该只从文本输入中完成。但是,我认为,postgresql与处理文本表示的数据类型联系太强烈。因此,例如,如果我想从三个整数值(D,M,Y)构建一个日期,我必须(如果我没有记错)构建一个字符串并让PG解析它。我觉得这样做不干净......

除此之外,我怀疑这会降低你的表现。

答案 1 :(得分:1)

哇。我很惊讶,但使用this page中的函数 - 特别是从三个整数构建日期值的函数 - 实际上没有更多地暴露内部C日期函数,实际上要快得多。对我来说,基准测试表明,以这种方式创建日期要快得多。

第一个是“dateserial”函数的实现:

postgres=# select to_date(a,1,3) 
postgres-# from generate_series(100,1000000) as v(a);

Time: 1365.851 ms

postgres=# select (a::text||'-01-03')::date from 
postgres-# generate_series(100,1000000) as v(a);

Time: 3454.224 ms

完整解决方案

修改dateserial.c

#include "postgres.h"
#include "utils/date.h"
#include "utils/nabstime.h"

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

Datum dateserial(PG_FUNCTION_ARGS);

PG_FUNCTION_INFO_V1 (dateserial);

Datum
dateserial(PG_FUNCTION_ARGS) {
  int32 p_year = PG_GETARG_INT32(0);
  int32 p_month = PG_GETARG_INT32(1);
  int32 p_day = PG_GETARG_INT32(2);

  PG_RETURN_DATEADT( date2j( p_year, p_month, p_day ) - POSTGRES_EPOCH_JDATE );
}

修改Makefile

MODULES = dateserial
PGXS := $(shell pg_config --pgxs)
include $(PGXS)

修改inst.sh(可选):

#!/bin/bash

make clean && make && strip *.so && make install && /etc/init.d/postgresql-8.4 restart

运行bash inst.sh

创建SQL函数dateserial

CREATE OR REPLACE FUNCTION dateserial(integer, integer, integer)
  RETURNS date AS
'$libdir/dateserial', 'dateserial'
  LANGUAGE 'c' IMMUTABLE STRICT
  COST 1;
ALTER FUNCTION dateserial(integer, integer, integer) OWNER TO postgres;

测试功能:

SELECT dateserial( 2007::int, 5, 5 )

答案 2 :(得分:0)

另一种方法是在连接上创建function index。这适用于没有更好数据类型的更一般情况。