2013-05-03 113 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

  • 我會爲工作日保存一個整數而不是當天的名字。