查询内部函数返回NULL,查询返回一个值

时间:2013-05-03 18:51:54

标签: sql postgresql plpgsql datetime-format

免责声明:我是PostgreSQL的新手,甚至更新于plpgSQL中的函数。我已经阅读了文档,但我很难过。

无论如何,我有这个功能,从表中选择一个充电带,用于星号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
============================

“day”是工作日的名称,band是电话的收费频段(“Peak”,“Off Peak”,“Weekend”),开始和结束是该收费频段的开始和结束时间

当我跑步时

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

我得到一个NULL结果。

当我跑步时

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

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

我很困惑。有人想告诉我哪里出错了吗?

3 个答案:

答案 0 :(得分: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 ...更常见的事情

答案 1 :(得分: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'的{​​{3}} to_char()给了你,我引用:

      

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

    要删除空白填充,有一个 template pattern FM。所以: 'FMDay' 。无需trim()

  • YYYY-MM-DD HH24:MI:SS是标准的ISO 8601格式,适用于任何区域设置。没有充分的理由使用varchar,首先将其设为 timestamp 。是的,时间点的数据类型在Postgres中称为时间戳(不是datetime)。这就是我选择参数名call_ts的原因。

  • 请勿使用未加引号的混合大小写标识符。永远。这只会导致混乱。它们全都是小写的。这是我选择call_ts

  • 的下一个原因
  • 请勿使用to_char()time获取timestamp。只需施放。更简单,更快捷。的 call_ts::time

  • 根本不需要 plpgsql 这个简单的查询,使其成为简单的 SQL 函数或只使用查询。

    < / LI>
  • 在plpgsql中,最好让SQL命令工作,而不是大量的赋值(相对较贵)。即使我没有将它转换为LANGUAGE sql,我也会使用这个单一的紧凑查询。

  • 没有 LIMIT 1
  • ORDER BY会为您提供可随时更改的任意结果。如果您关心的是哪一行,或者您希望它至少保持稳定,则添加ORDER BY

  • start < x AND finish > x的时间间隔是一种可疑的做法。通常您希望包含下限,因此我使用 <=

  • 您考虑过template pattern modifier吗?

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

答案 2 :(得分:0)

我很惊讶任何一个查询都有效,如果day包含'2013-05-03'而不是'Friday'...你的SQL没有问题,但是你的架构似乎肯定存在问题。 / p>

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');