Sql 使用整数值的日期算法 问题

Sql 使用整数值的日期算法 问题,sql,postgresql,query-optimization,Sql,Postgresql,Query Optimization,字符串连接正在减慢查询速度: date(extract(YEAR FROM m.taken)||'-1-1') d1, date(extract(YEAR FROM m.taken)||'-1-31') d2 这在代码中作为字符串的一部分实现,如下所示(其中p变量是整数,由最终用户作为输入提供): 查询的这一部分在有日期的情况下运行3.2秒,没有日期的情况下运行1.5秒,这使我相信有足够的改进空间 查询的总运行时间小于10秒;我希望将整个查询时间缩短到2或3秒左右。硬件升级已经发生。;-) 版

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

date(extract(YEAR FROM m.taken)||'-1-1') d1,
date(extract(YEAR FROM m.taken)||'-1-31') d2
这在代码中作为字符串的一部分实现,如下所示(其中
p
变量是整数,由最终用户作为输入提供):

查询的这一部分在有日期的情况下运行3.2秒,没有日期的情况下运行1.5秒,这使我相信有足够的改进空间

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

版本 PostgreSQL 8.4.4

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

更新 这看起来很有希望:


非常感谢

另一种选择是在连接上创建一个。这在没有更好的数据类型可用的更一般的情况下有效。

遗憾的是,我认为没有其他方法来构建没有文本连接的日期

是的,坦率地说,我不喜欢这里的邮局。似乎大多数日期操作必须通过将日期字段提取为整数、将其转换为文本、将其附加到更多文本以创建日期的文本表示形式,然后告诉postgres将该文本解析为日期来完成。。。这对我来说很难闻,我不自觉地觉得通过解析字符串来构建日期应该只从文本输入来完成。但是,我认为,postgresql将处理文本表示的数据类型联系得太紧密了。因此,例如,如果我想从三个整数值(D,M,Y)构建一个日期,我必须(如果我没有弄错的话)构建一个字符串并让PG解析它。我觉得这样做很不干净


抛开咆哮不谈,我怀疑这会大大降低你的表现。

哇。我很惊讶,但是使用来自的函数——特别是从三个整数构建日期值的函数——实际上不做更多的事情——公开内部的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
SELECT dateserial( 2007::int, 5, 5 )
全解 编辑
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
(可选):

运行
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;
测试功能:

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
SELECT dateserial( 2007::int, 5, 5 )

非常感谢。我想在这种情况下我做不到;文档显示了
((first_name | | |“”| | last_name))
,但是我没有使用表中的月和日:我使用表中的年和使用表中的月/日创建日期。我应该索引哪些列?@leonbloy:使用
date('1960-1-1')d1
,性能为2.6s。使用
date('1960'| |'-1-1')d1
,性能为4.3s。@leonbloy:use
date('1960'| |'-1-1')d1
,性能为3.0s。使用date('1960-1-1')vs date('extract(从m.take开始的年份)|-1-1'))d1听起来像是使用常量比使用包含列值的表达式要快——这并不奇怪。听起来并不是连接的问题…@rfusca:Fair;但问题仍然存在。我必须先把整数转换成文本,然后再转换成日期。“肯定有更好的方法吗?”戴夫·贾维斯-我不认为有更好的方法,至少对于你的那一段提问。我的两分钱。是的。字符串串联是(相对)巨大的性能损失。我实现了dateserial函数,现在正在使用它。@Dave Jarvis-我期望得到惩罚,只是没有我看到的那么多。
dateserial(摘录(从m.take开始的年份),1,1)
;我应该通过使用
dateserial(m.take,1,1)
(在dateserial函数中提取年份)获得另一个性能提升。在函数中提取年份只需要1/10秒的改进;不值得这么努力。