获取UNIQUE ID的整数之和

时间:2014-06-17 14:53:22

标签: postgresql join database-design plpgsql postgresql-9.3

在使用PostgreSQL 9.3作为后端的游戏中,我试图限制用户每周玩的游戏数量。

我准备了an SQL Fiddle,但不幸的是它没有用。

我的(测试,而不是生产)代码在这里:

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 = _id然后第二部分id in (...) - 这将是给我2次游戏次数。

请问有什么治疗方法吗?

我需要"检测"在上述条件下使用id两次时。

2 个答案:

答案 0 :(得分:1)

我会尝试某种数据透视表,虽然我不知道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;

然后获得最多两个值。

但是一个用户可以使用超过1个IP进行播放,这里没有介绍(很可能你需要记录每个游戏的IP来捕捉这些情况)

另请注意,这将是非常低性能的代码。子查询将在过滤阶段的每个匹配行上运行。

答案 1 :(得分:1)

表格布局

请勿使用char(7)存储时间戳。

准确地说,请勿使用char(7)存储任何内容。永远。详细信息:
Compare varchar with char

并且不要将日期/时间数据存储在任何文本表示中。使用timestamp or date

如果您只对一年中的感兴趣,那么您只需存储一个integer(甚至是smallint),您可以使用{{{ 3}}:

SELECT extract(week FROM now())::int;

但我建议存储占用4个字节的date,就像integer一样,而char(7)占用11个字节。您可以便宜地提取具有上述功能的周。或者使用extract()

SELECT date_trunc('week', now())

如果必须,id更应该是int - 或bigintvarchar(32)效率很低。

并声明您的专栏completed NOT NULL!或者你必须处理可能的NULL值。您的检查约束覆盖它。 NULL不违反约束。

查询/功能

假设date的数据类型ywint的{​​{1}}:

id

如果定义了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,则根本不需要NOT NULL作为参数。只需_id