验证时间戳为空或过去

时间:2011-12-29 16:47:56

标签: postgresql plpgsql postgresql-8.4

在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

如果vip已过期,则< 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 语句将“抛出异常”并且事务将被回滚)?

更新

感谢您的所有回复,我也有一个提示:

          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在5月份之前发了vip,应该可以给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;

3 个答案:

答案 0 :(得分:2)

将时间戳与可能为空的内容进行比较时,可以使用<?php $displayed = []; // get number of messages from a specific user to the logged in user $get_mess = mysqli_query ($connect, "SELECT * FROM private_messages WHERE message_to = '$username' AND message_from ='$user'"); $num_msgs = mysqli_num_rows($get_mess); // getting all the conversations which concern the user logged on. $con = mysqli_query ($connect, "SELECT * FROM private_messages WHERE message_from='$username' OR message_to='$username'"); while ($get_con = mysqli_fetch_assoc($con)){ $msg_from = $get_con['message_from']; $msg_to = $get_con['message_to']; // get other persons firstname $u_name = mysqli_query($connect, "SELECT * FROM users WHERE username ='$msg_to'"); $get_cu = mysqli_fetch_assoc($u_name); $got_ufn = $get_cu['first_name']; $got_uln = $get_cu['last_name']; if ($msg_to == $username || $msg_from == $username){ if(!in_array($msg_to, $displayed)) { echo "<li class='list' role='presentation'> <div class='parent'> <div class='disp_pic'> <img class='img-rounded' src='$profile_pic2'/> </div> <div class='user_d'> <a href='messages.php?u=$msg_from'> $got_ufn $got_uln</a> </div>"; if ($num_msgs == 0){ // dont display badge }else { echo "<span id='num_of_msgs_from' class='badge'>"; if ($user == $user){ $num_msgs == 0; echo "$num_msgs </span>"; }else { echo " $num_msgs </span>"; } } echo" </div> </li>"; $displayed[] = $msg_to; } } } // while closed ?> 函数提供“默认”值进行比较。例如,

COALESE

select 1 from pref_users where id=_from and vip > COALESCE(current_timestamp, to_timestamp(0)); 函数从列表中返回第一个非空值。见docs。 (还有to_timestamp() docs ...)

答案 1 :(得分:1)

可能有更优雅的解决方案,但我认为

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

会做这项工作。 我想你的问题与NULL既不是真的也不是假的有关。

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

答案 2 :(得分:1)

空值在任何比较时返回false为null(因此,其中null&lt;&gt; 1为false且null = 1为false ... null永远不等于或不等于任何东西)。而不是

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,则返回null。如果has_vip为null然后返回,你可以去吗?