2011-12-29 46 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()文档...)