2011-12-29 45 views
2

在PostgreSQL 8.4.9我有一個小遊戲,用戶可以購買一個VIP(「非常重要的人」)狀態:驗證時間戳爲空或過去

# \d pref_users; 
       Table "public.pref_users" 
    Column |   Type    | Modifiers 
------------+-----------------------------+--------------- 
id   | character varying(32)  | not null 
vip  | timestamp without time zone | 

如果VIP從未購買它將是NULL

如果貴賓已過期,它將是< CURRENT_TIMESTAMP

我試圖創建PL/pgSQL的程序,允許有足夠的VIP身份的用戶留下了一個星期給其他用戶,作爲「禮物」:

create or replace function pref_move_week(_from varchar, 
    _to varchar) returns void as $BODY$ 
     declare 
       has_vip boolean; 
     begin 

     select vip > current_timestamp + interval '1 week' 
     into has_vip from pref_users where id=_from; 

     if (not has_vip) then 
       return; 
     end if; 

     update pref_users set vip = current_timestamp - interval '1 week' where id=_from; 
     update pref_users set vip = current_timestamp + interval '1 week' where id=_to; 

     end; 
$BODY$ language plpgsql; 

不幸的是這個過程不工作如果授予用戶的vip值爲NULL,則正確:

# update pref_users set vip=null where id='DE16290'; 
UPDATE 1 

# select id,vip from pref_users where id in ('DE16290', 'DE1'); 
    id |   vip 
---------+---------------------------- 
DE1  | 2012-01-05 17:35:17.772043 
DE16290 | 
(2 rows) 

# select pref_move_week('DE16290', 'DE1'); 
pref_move_week 
---------------- 

(1 row) 

# select id,vip from pref_users where id in ('DE16290', 'DE1'); 
    id |   vip 
---------+---------------------------- 
DE1  | 2012-01-05 17:43:11.589922 
DE16290 | 2011-12-22 17:43:11.589922 
(2 rows) 

IF - 上面的陳述似乎不起作用,並通過。

另外我不知道has_vip變量是否需要在這裏呢?

和我如何能確保主鍵_from_TO是在pref_users表確實存在,或者是這個已經照顧(因爲UPDATE的一個語句將「拋出一個異常「並且交易將被回滾)?

UPDATE:

感謝您對所有的答覆和我也有一個提示使用方法:

  if (not coalesce(has_vip, false)) then 
        return; 
      end if; 

但現在我有一個新的問題:

# select id,vip from pref_users where id in ('DE16290', 'DE1'); 
    id |   vip 
---------+---------------------------- 
DE1  | 2012-01-05 17:43:11.589922 
DE16290 | 
(2 rows) 

(即DE1有貴賓到五月,應該能夠每週給DE16290,但):

# select pref_move_week('DE1', 'DE16290'); 
pref_move_week 
---------------- 

(1 row) 

# select id,vip from pref_users where id in ('DE16290', 'DE1'); 
    id |   vip 
---------+---------------------------- 
DE1  | 2012-01-05 17:43:11.589922 
DE16290 | 
(2 rows) 

(出於某種原因,一切都沒有改變?)

更新2:最終的解決方案 -

create or replace function pref_move_week(_from varchar, 
     _to varchar) returns void as $BODY$ 
      begin 

      select 1 from pref_users 
       where id=_from and 
       vip > current_timestamp + interval '1 week'; 

      if not found then 
        return; 
      end if; 

      update pref_users set 
       vip = vip - interval '1 week' 
       where id=_from; 

      update pref_users set 
       vip = greatest(vip, current_timestamp) + interval '1 week' 
       where id=_to; 

      end; 
    $BODY$ language plpgsql; 

回答

1

Null值上的任何比較短返回false的爲空(所以其中null <> 1爲假,並且空= 1爲假...空不等於或不等於任何東西)。取而代之的

if (not has_vip) then return 

if (not has_vip) or has_vip is null then return 

你正在使用的語法是這裏有點獨特的,我不習慣於閱讀它...猜你來自不同的背景比SQL並且還沒有遇到null樂趣。 Null是SQL中的一個特殊概念,您需要以不同的方式處理。記住它不等於空白或0,也不小於或大於任何數字。

編輯在: 我有點這個困惑:

select vip > current_timestamp + interval '1 week' 
    into has_vip from pref_users where id=_from; 

我相信這是等同於:

select vip 
    into has_vip from pref_users 
    where id=_from 
    and vip > current_timestamp + interval '1 week'; 

我希望如此。現在,如果沒有找到記錄或者VIP值爲空,這將返回null。你可以去,如果has_vip爲空,然後返回?

+0

謝謝 - 是的,我有Perl,PHP,Java,C的經驗 - 但每當我必須使用SQL(除了基本的選擇/更新/刪除語句)之外,我非常努力並且不得不四處詢問。有關我更新的問題的任何建議嗎? – 2011-12-29 20:06:09

+1

編輯我的答案......假設我已經遵循了這個邏輯,我認爲如果VIP爲空則返回else繼續。有點有趣的是,如果(不是has_vip)讓我對它應該返回的東西感到困惑。 – Twelfth 2011-12-29 20:38:02

+0

你的方式更好,謝謝 – 2011-12-29 20:57:57

1

有可能是一個更優雅的解決方案,但我認爲

SELECT (vip IS NOT NULL) AND (vip > current_timestamp + interval '1 week') 

會做這項工作。 我猜你的問題與NULL不是真或假都是有關的。

您可以在the docs找到更多信息。

+0

我想另一種解決方案將做「如果(has_vip IS NULL)或(不has_vip)」 – Arthur 2011-12-29 18:57:30

+0

我很困惑,首先你已經使用SELECT(在一個IF ... THEN .... END IF有條件的,正確的?),但在你的第二個建議你不使用該關鍵字? – 2011-12-29 20:07:30

+1

確實可能不是很清楚。在我的第一個答案中,我指的是將select_vip> current_timestamp + interval'1 week'引入has_vip from pref_users where id = _from;「。你真正想要的是「select(vip IS NOT NULL)AND(vip> current_timestamp + interval'1 week')into pre_users where has_vip where id = _from;」這將在每種情況下返回一個布爾值:如果vip爲null或者它不爲null並且在一週之前爲false;並且如果它不是空的並且在一週之後,則爲真。實際上,這正是你問題標題的答案。注意「vip IS NOT NULL」部分返回bool – Arthur 2011-12-30 10:31:48

2

將您的時間戳與可能爲空的內容進行比較時,可以使用COALESE函數提供「默認」值進行比較。例如,

select 1 from pref_users 
    where id=_from and 
    vip > COALESCE(current_timestamp, to_timestamp(0)); 

COALESCE函數返回其列表中的第一個非空值。見docs。 (也to_timestamp()文檔...)