Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/date/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Postgresql 获取唯一ID的整数和_Postgresql_Join_Database Design_Plpgsql_Postgresql 9.3 - Fatal编程技术网

Postgresql 获取唯一ID的整数和

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

在使用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 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;