#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 Обновлен исправлением синтаксиса. Всегда публикуйте точное сообщение об ошибке.