2011-12-29 6 views
2

は、私は、ユーザーがVIP(「非常に重要な人物」)を購入することができる小型ゲーム、ステータスを持っているに:タイムスタンプの検証はnullであるかのPostgreSQL 8.4.9で過去

# \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になります。

私は十分VIPステータスを持つユーザーは、「贈り物」として、他のユーザーにそれの週を与えるために残さできるようにPL/pgSQLのプロシージャを作成しようとしています

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_topref_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は5月までVIPを持ち、DE16290に1週間与えることができるはずですが):

# 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) 

(いくつかの理由については何も変わっていない?)

UPDATE 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がfalseで、null = 1がfalseの場合)。nullは等しくないか、等しくない)。代わりに

if (not has_vip) then return 

の使用している構文は、ここで少し独特であり、私はそれを読んでに慣れていないよ...あなたがより別の背景から来ている推測

if (not has_vip) or has_vip is null then return 

で行きますSQLとnull funをまだ遭遇していない。 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で戻り値が返ってきたらちょうどいいですか?

+0

ありがとうございます - はい、私はPerl、PHP、Java、Cの経験がありますが、SQLを使用する必要があるときはいつでも(私は基本的なselect/update/delete文のほかに)苦労しています。更新された質問のアドバイスをお願いしますか? –

+1

私の答えを編集しました...私はその論理に従ったと仮定して、私はあなたがVIPがヌルであれば逃げることができると思います。ちょっと面白いこと(has_vipでない)が返すと思われるものについて私が混乱している場合。 – Twelfth

+0

あなたの方法はより良いです、ありがとう –

1

あり、よりエレガントな解決策になるが、私は

SELECT (vip IS NOT NULL) AND (vip > current_timestamp + interval '1 week') 
を考えること

は作業を行います。 あなたの問題は、NULLが真または偽でないという事実に関係していると思います。

詳細はthe docsにあります。

+0

と私は別の解決策は "if(has_vip IS NULL)OR(has_vipではない)"と思うでしょう。 – Arthur

+0

私は混乱しています。 IF ... THEN .... END IF条件付き、正しい?)しかし、2番目の提案では、そのキーワードは使用しないでください。 –

+1

確かにそれはあまり明確ではないかもしれません。私の最初の答えでは、あなたの "select vip> current_timestamp + intervalを" pref_usersからのhas_vipに1週間 "と呼んでいました。あなたが本当に望むのは、pref_usersからid_ _from;からhas_vipに "select(vip IS NOT NULL)AND(vip> current_timestamp + interval '1 week')です。これは、すべての場合でブール値を返します.vipがnullの場合はfalse、またはnullでなく1週間前の場合はfalseです。ヌルでない場合は1週間後にtrueを返します。実際、これはあなたの質問のタイトルに対する答えです。 "vip IS NOT NULL"部分がboolを返すことを覚えておいてください。 – Arthur

2

タイムスタンプをnullの可能性のあるものと比較するときは、COALESE関数を使用して、比較する「デフォルト」値を指定できます。例えば、

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

COALESCE関数は、そのリストから最初の非ヌルを戻します。 docsを参照してください。 (to_timestamp() docs ...)

関連する問題