2

免责声明:我是 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”),start 和 finish 是该收费区间的开始和结束时间。

当我跑

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;

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

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

4

3 回答 3

1

因为to_char(..., 'Day')返回一个末尾有空格填充的字符串。包装分配给callDayin的表达式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 ...

于 2013-05-03T22:22:55.813 回答
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'to_char()我引用:

    完整的大写日期名称(空白填充到 9 个字符)

    要删除空白填充,有一个模板模式修饰符: FM。所以做那个:'FMDay'。不需要trim()

  • YYYY-MM-DD HH24:MI:SS是标准 ISO 8601 格式,适用于任何语言环境。没有充分的理由使用 a varchar,从 atimestamp开始。是的,时间点的数据类型在 Postgres 中称为时间戳(不是datetime)。这就是我为它选择参数名称call_ts的原因。

  • 不要使用不带引号的混合大小写标识符。曾经。它只会导致混乱。它们都被转换为小写。这就是我选择的下一个原因call_ts

  • 不要用于从 ato_char()中获取a 。刚。更简单,更快。timetimestampcall_ts::time

  • 对于这个简单的查询,你根本不需要plpgsql,使它成为一个普通的SQL函数或者只使用查询。

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

  • LIMIT 1withoutORDER BY给你一个可以随时改变的任意结果。如果您完全关心获得哪一行,或者您希望它至少稳定,您可以添加ORDER BY

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

  • 你考虑过时区吗?

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

于 2013-05-04T15:03:04.913 回答
0

如果 day 包含“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');
于 2013-05-03T19:56:52.273 回答