免责声明:我是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;
我得到正确答案(对于我的数据):'峰值'
我很困惑。有人想告诉我哪里出错了吗?
答案 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
的时间间隔是一种可疑的做法。通常您希望包含下限,因此我使用 <=
。
我会为工作日而不是当天的名字保存一个整数。
答案 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');