2013-05-03 153 views
2

声明:我是PostgreSQL的新手,甚至更新到plpgSQL中的函数。我读过文档,但我很难过。函数内部的查询返回NULL,查询返回值

无论如何,我有这个功能,选择一个负责带出表的星号CDR的评价:

CREATE FUNCTION getBand(
    callDateTime varchar 
) RETURNS varchar AS $$ 
/* Select correct charging band */ 
DECLARE 
    callTime varchar; 
    callDay varchar; 
    theBand varchar; 
BEGIN 
    /* Find the time and the day of the call */ 
    callTime := to_char(to_timestamp(callDateTime,'YYYY-MM-DD HH24:MI:SS'),'HH24:MI:SS'); 
    callDay := to_char(to_timestamp(callDateTime,'YYYY-MM-DD HH24:MI:SS'),'Day'); 
    theBand := band 
    FROM bands 
    WHERE day = callDay 
    AND start < callTime 
    AND finish > callTime 
    LIMIT 1; 
    RETURN theBand; 
END; 
$$ LANGUAGE plpgsql 

输入字符串从星号CDR表解除了,格式为“YYYY -MM-DD HH24:MI:SS'。

的带表的格式为

day | band | start | finish 
============================ 

其中“天”是一个工作日的名称,乐队是一个调用(“峰”充电带,“非高峰期”,“周末” )并且开始和结束是该充电带的开始和结束时间。

当我运行

SELECT getBand('2013-05-03 11:30:00'); 

我得到一个空的结果。

当我运行

SELECT band FROM bands 
WHERE day = 'Friday' AND start < '11:30:00' and finish > '11:30:00' LIMIT 1; 

我得到正确的答案(我的数据): '峰值'

我很困惑。任何人都想告诉我我哪里出错了?

回答

0

令我惊讶的是,如果一天中包含'2013-05-03'而不是'Friday',那么任一查询都可以正常工作......您的SQL没有问题,但您的模式似乎确实存在问题。

Postgres有一个范围类型,而这可能是你应该使用什么这里:

create table bands (
    id serial primary key, 
    name varchar, 
    playing tsrange 
); 

insert into bands (name, playing) values ('test', '[2013-05-03 11:00:00, 2013-05-03 12:00:00]'); 

select name from bands where playing @> '2013-05-03 11:30:00'::timestamp; 

create or replace function getBand(_datetime timestamp) returns varchar as $$ 
    select name from bands where playing @> _datetime limit 1; 
$$ language sql; 

select getBand('2013-05-03 11:30:00'); 
+0

请在回答之前仔细阅读该问题。输入字符串格式为'YYYY-MM-DD HH24:MI:SS',因为它来自Asterisk CDR表。 “频段”表是呼叫的收费频段列表,用于确定“峰值”,“非高峰”和“周末”通话资费何时处于活动状态。该表具有格式“乐队”,“日”,“开始”,“结束”。有效行的例子是“星期一”,“峰值”,“08:00:00”,“18:00:00”。 – 2013-05-03 20:46:08

1

因为to_char(..., 'Day')返回具有空间填充在结束的字符串。包分配到callDay表达的trim()

callDay := trim(to_char(to_timestamp(callDateTime,'YYYY-MM-DD HH24:MI:SS'),'Day')); 

(我终于找到了这通过添加raise info 'calltime="%", callday="%"', calltime, callday虽然我去了不少死角第一)

(这是相当混乱:theBand := band FROM bands ... - 这将是更通常的写SELECT band INTO theBand FROM bands ...

+0

我做了你的建议,它没有奏效。然后我发现我在'错误的地方'有'<' and '>'的标志......没有行被拾起。我知道这是我自己的愚蠢行为,但我不认为这是基本的...... – 2013-05-03 22:54:22

+0

您可能对'FM'修饰符感兴趣。 – 2013-05-07 02:04:09

1
CREATE FUNCTION get_band(call_ts timestamp) 
    RETURNS text AS 
$func$ 
    SELECT band 
    FROM bands 
    WHERE day = to_char(call_ts,'FMDay') 
    AND start <= call_ts::time 
    AND finish > call_ts::time 
    ORDER BY start 
    LIMIT 1 
$func$ LANGUAGE sql STABLE; 
  • 的的 'Day'给你,我引用:

    全大写日期名(空白填充为9个字符)

    要卸下填补空白是有template pattern modifierFM。所以说:'FMDay'。无需trim()

  • YYYY-MM-DD HH24:MI:SS是标准的ISO 8601格式,这与任何现场工作。没有很好的理由使用varchar,使其成为timestamp开始。是的,某个时间点的数据类型在Postgres中称为时间戳(不是datetime)。这就是我为什么选择参数名称call_ts的原因。

  • 请勿使用未加引号的,混合大小写标识符。永远。这只会导致混乱。他们全部投下小写。这是我选择call_ts的下一个原因。

  • 请勿使用to_char()timestamp获得time。只需转换。更简单,更快。 call_ts::time

  • 你不需要PLPGSQL这个简单的查询在所有,使它成为一个普通的SQL功能或者仅使用查询。

  • 在plpgsql中,最好让SQL命令去工作,而不是很多小的作业(它们比较昂贵)。即使我没有将它转换为LANGUAGE sql,我也会使用这个简单的查询。

  • LIMIT 1没有ORDER BY给你一个任意结果,可随时更改。如果你关心的是你得到的那一排,或者你希望它至少稳定下来,那么你可以添加ORDER BY

  • 这是一个可疑的做法与start < x AND finish > x有一个时间间隔。通常你想包括下限,所以我用<=

  • 你觉得time zones

  • 我会为工作日保存一个整数而不是当天的名字。