Postgresql 获取唯一ID的整数和
在使用PostgreSQL 9.3作为后端的游戏中,我试图限制用户每周玩的游戏数量 我已经准备好了,但不幸的是它不起作用 我的(测试,而非生产)代码如下:Postgresql 获取唯一ID的整数和,postgresql,join,database-design,plpgsql,postgresql-9.3,Postgresql,Join,Database Design,Plpgsql,Postgresql 9.3,在使用PostgreSQL 9.3作为后端的游戏中,我试图限制用户每周玩的游戏数量 我已经准备好了,但不幸的是它不起作用 我的(测试,而非生产)代码如下: create table pref_users ( id varchar(32) primary key, last_ip inet ); create table pref_match ( id varchar(32) references pref_users on delete casc
create table pref_users (
id varchar(32) primary key,
last_ip inet
);
create table pref_match (
id varchar(32) references pref_users on delete cascade,
completed integer default 0 check (completed >= 0),
yw char(7) default to_char(current_timestamp, 'IYYY-IW'),
primary key(id, yw)
);
下面是一个存储过程,我试图用它查找本周玩的游戏数:
create or replace function pref_get_user_info(
IN _id varchar,
IN _last_ip inet,
OUT games_this_week integer
) as $BODY$
begin
select sum(completed)
into games_this_week
from pref_match where
(id = _id or
id in (select id from pref_users where last_ip=_last_ip)) and
yw=to_char(current_timestamp, 'IYYY-IW');
end;
$BODY$ language plpgsql;
在这种情况下:
(id = _id or
id in (select id from pref_users where last_ip=_last_ip))
我试图抓住那些试图作弊的用户,他们使用不同的玩家id
但来自同一IP地址加入游戏
但我担心,有时我会得到双倍的完成游戏数-因为在上述条件下,第一部分将匹配:id=\u id
,然后第二部分id在(…)
-这将给我两倍的游戏数
请问有什么治疗方法吗
在上述情况下,我需要“检测”两次使用
id
。我会尝试某种透视表,尽管我不知道plpgsql是否支持它
begin
select
SUM
(
CASE WHEN pref_match.id IN (select id from pref_users where last_ip=_last_ip)
THEN completed
) AS ip_matches,
SUM
(
CASE WHEN pref_match.id = _id
THEN completed
) AS id_matches,
into games_this_week
from pref_match
and yw=to_char(current_timestamp, 'IYYY-IW');
end;
然后获取两个值的最大值
但是一个用户可以从多个IP玩游戏,这里不包括这一点(很可能您需要记录每个游戏IP以捕捉这些情况)
还要注意的是,这将是非常低性能的代码。子查询将在筛选阶段的每个匹配行上运行。表布局
不要使用char(7)
存储时间戳
准确地说,不要使用char(7)
存储任何内容。曾经详细信息:并且不要以任何文本表示形式存储日期/时间数据。使用 如果您只对一年中的一周感兴趣,您可以只存储一个
整数
(甚至是smallint
),您可以通过以下方式获得:
但是我建议存储一个日期
,它占用4个字节,就像整数
,而字符(7)
占用11个字节。您可以使用上述功能廉价地提取周。或使用:
如果必须的话,id
应该是int
-或者bigint
varchar(32)
效率相当低
并声明您的列已完成
不为空
!或者,您必须处理可能的空值。您的检查约束不包括这一点。NULL不违反约束
查询/功能
假设yw
的数据类型为date
,而id
的数据类型为int
:
CREATE OR REPLACE FUNCTION pref_get_user_info(_id int, _last_ip inet
,OUT games_this_week int) AS
$func$
DECLARE
_this_monday date := date_trunc('week', now())::date;
BEGIN
SELECT sum(completed)::int
INTO games_this_week
FROM pref_users u
JOIN pref_match m USING (id)
WHERE (u.id = _id OR u.last_ip = _last_ip)
AND m.yw BETWEEN _this_monday
AND _this_monday + 6; -- "sargable"
END
$func$ LANGUAGE plpgsql;
如果定义了
last_ip
notnull
,则根本不需要\u id
作为参数。刚刚\u last\u ip
谢谢-我知道,我应该使用时间戳而不是字符串,但我在4年前做出了错误的决定,现在无法更改数据库。。。为什么建议将notnull
添加到completed
?默认值0是否足够?@AlexanderFarber:如果要确保列从不为NULL,请将其定义为非NULL
。默认值是不够的-仍然可以通过这种方式输入NULL。还有助于存储和查询规划。
SELECT date_trunc('week', now())
CREATE OR REPLACE FUNCTION pref_get_user_info(_id int, _last_ip inet
,OUT games_this_week int) AS
$func$
DECLARE
_this_monday date := date_trunc('week', now())::date;
BEGIN
SELECT sum(completed)::int
INTO games_this_week
FROM pref_users u
JOIN pref_match m USING (id)
WHERE (u.id = _id OR u.last_ip = _last_ip)
AND m.yw BETWEEN _this_monday
AND _this_monday + 6; -- "sargable"
END
$func$ LANGUAGE plpgsql;