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

如果 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;
4

3 回答 3

2

将您的时间戳与可能为空的内容进行比较时,您可以使用该COALESE函数提供一个“默认”值进行比较。例如,

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

COALESCE函数从其列表中返回第一个非空值。请参阅文档。(还有to_timestamp()文档...)

于 2016-03-12T17:25:13.943 回答
1

There may be a more elegant solution but I think

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

would do the work. I guess your problem is related to the fact that NULL is neither true or false.

You may find more info in the docs.

于 2011-12-29T18:54:46.027 回答
1

Null 值在任何不为 null 的比较中返回 false(因此 null <> 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 为空然后返回,你可以去吗?

于 2011-12-29T18:55:34.760 回答