Как ОБЪЕДИНИТЬ столбец временных меток?

#postgresql #timestamp #postgresql-9.3

#postgresql #временная метка #postgresql-9.3

Вопрос:

В PostgreSQL 9.3 у меня есть следующая таблица с 2 временными метками:

 create table pref_users (
        id varchar(32) primary key,
        first_name varchar(64) not null,
        last_name varchar(64),
        female boolean,
        avatar varchar(128),
        city varchar(64),
        mobile varchar(64),
        login timestamp default current_timestamp,
        logout timestamp,
        last_ip inet,
        vip timestamp,       /* XXX can be NULL */
        grand timestamp,     /* XXX can be NULL */
        mail varchar(256),
        green integer,
        red integer,
        medals integer not null default 0
);
 

Временные метки vip и grand укажите, заплатили ли пользователи моей игры за определенные привилегии — до этих дат.

Когда пользователь подключается к моему игровому серверу, я вызываю следующую процедуру с OUT параметрами:

 create or replace function pref_get_user_info(
    IN _id varchar,
    OUT is_banned boolean,
    OUT is_grand boolean,
    OUT is_vip boolean,
    OUT rep integer
) as $BODY$
        begin
            is_banned := exists(select 1 from pref_ban where id=_id);

            if is_banned then
                return;
            end if;

            select
                grand > current_timestamp,
                vip > current_timestamp,
            into is_grand is_vip
            from pref_users where id=_id;

            if is_grand or is_vip then
                return;
            end if;

            select
                count(nullif(nice, false)) -
                count(nullif(nice, true))
            into rep
            from pref_rep where id=_id;
        end;
$BODY$ language plpgsql;
 

Это работает хорошо, но иногда доставляет NULL значения моему игровому демону (скрипту Perl):

 # select * from pref_get_user_info('OK674418426646');
 is_banned | is_grand | is_vip | rep
----------- ---------- -------- -----
 f         |          |        | 126
(1 row)
 

Мне не нужен NULL хотя (и он выводит предупреждение в моем скрипте Perl) — мне просто нужны значения «true» или «false».

Итак, я попытался:

 select
    coalesce(grand, 0) > current_timestamp,
    coalesce(vip, 0) > current_timestamp,
into is_grand is_vip
from pref_users where id=_id;
 

Но это выдает ошибку:

 # select * from pref_get_user_info('OK674418426646');
ERROR:  COALESCE types timestamp without time zone and integer cannot be matched
LINE 2:                         coalesce(grand, 0) > current_timesta...
                                                ^
QUERY:  select
                        coalesce(grand, 0) > current_timestamp,
                        coalesce(vip, 0) > current_timestamp,
                                  is_vip
                    from pref_users where id=_id
CONTEXT:  PL/pgSQL function pref_get_user_info(character varying) line 9 at SQL statement
 

Поэтому мне интересно, что здесь делать, пожалуйста?

Мне действительно нужно

 select
    coalesce(grand, current_timestamp - interval '1 day') > current_timestamp,
    coalesce(vip, current_timestamp - interval '1 day') > current_timestamp,
into is_grand is_vip
from pref_users where id=_id;
 

или, может быть, есть более приятный способ (например, «начало эпохи» или «вчера»)?

Обновить:

По предложению Клодоальдо Нето (спасибо!) Я пробовал:

 select
coalesce(grand > current_timestamp, false),
coalesce(vip > current_timestamp, false),
into is_grand is_vip
from pref_users where id=_id;
 

но is_vip равно NULL, когда vip равно NULL:

 # select * from pref_get_user_info('OK674418426646');
 is_banned | is_grand | is_vip | rep
----------- ---------- -------- -----
 f         | t        |        |
(1 row)
 

И когда я пытаюсь выполнить одно из следующих действий, я получаю синтаксическую ошибку:

 select
coalesce(grand > current_timestamp, false),
coalesce(vip > current_timestamp, false),
into is_grand, is_vip
from pref_users where id=_id;

select
coalesce(grand > current_timestamp, false),
coalesce(vip > current_timestamp, false),
into (is_grand, is_vip)
from pref_users where id=_id;
 

Как я могу SELECT использовать сразу 2 переменные здесь?

Комментарии:

1. В первой отредактированной версии удалите запятую перед «into» в конце третьей строки.

2. Ах да, глупый я (скопировал приведенную выше строку в vi-editor). Но как насчет запятой между двумя переменными? Оба способа принимаются в командной строке «psql», но дают разные результаты…

Ответ №1:

Если вы хотите логическое значение:

 coalesce(grand > current_timestamp, false)
 

Если вам нужно 0 или 1:

 coalesce((grand > current_timestamp)::integer, 0)
 

В вашем обновленном вопросе у вас есть дополнительная запятая между списком выбора и into предложением

 coalesce(vip > current_timestamp, false),
into is_grand, is_vip
 

Выньте его

 coalesce(vip > current_timestamp, false)
into is_grand, is_vip
 

Комментарии:

1. Это работает, потому current_timestamp что не может быть нулевым. Что бы вы сделали, если бы grand сравнивался с чем-то, что может быть нулевым. и ты хотел этого coalesce(date1,0) > coalesce(date2,0)

2. Всякий раз , когда хотя бы один из операндов имеет значение null , результат будет равен null , как в select null > null . Итак, ответ все еще применим.

3. 1 спасибо, но не могли бы вы, пожалуйста, проверить обновленный вопрос? Я не могу понять правильный синтаксис для выбора сразу в 2 переменные…

4. На самом деле это не совсем одно и то же. Чтобы проиллюстрировать возвращение к моему примеру, если бы вы захотели coalesce(date1,0) > coalesce(date2,0) , это можно было бы преобразовать в case when date1 is not null and date 2 is null then true when date1 is null then false else date1 > date2 end , с другой стороны colaesce(date1 > date2, false) , было бы case when date1 is null or date2 is null then false else date1 > date2 end . Если бы мы хотели избежать заявления case, есть ли более простой способ, чем сделать date diff.

5. @AlexanderFarber Обновлен исправлением синтаксиса. Всегда публикуйте точное сообщение об ошибке.