将每场比赛3名球员保存到PostgreSQL中

时间:2011-10-26 07:51:18

标签: sql postgresql inner-join plpgsql join

我正在尝试将大量card game次比赛(总是有3位玩家)保存到PotsgreSQL 8.4.9中

我几乎所有的东西(请在下面)只缺少2个小部件。

  1. 首先,我正在尝试创建一个用于保存分数的PL / pgSQL程序
  2. 然后我需要一个连接选择语句来检索比赛日期,球员姓名,性别,头像,分数以及球员是否过早退出比赛
  3. 我创建了3个SQL表:

        create table pref_users (
                uid varchar(32) primary key,
                first_name varchar(64),
                female boolean,
                avatar varchar(128)
        }
    
        create table pref_games {
                gid serial,
                rounds integer not null,
                finished timestamp default current_timestamp
        }
    
        create table pref_scores (
                uid varchar(32) references pref_users,
                gid serial references pref_games,  /* XXX serial ok here? */
                money integer not null,
                quit boolean
        );
    

    这是我的PL / pgSQL程序,我需要一些帮助:

        create or replace function pref_insert_scores(
            _uid0 varchar, _money0 integer, _quit0 boolean,
            _uid1 varchar, _money1 integer, _quit1 boolean,
            _uid2 varchar, _money2 integer, _quit2 boolean,
            _rounds integer) returns void as $BODY$
                begin
    
                insert into pref_games (rounds) values (_rounds);
    
                -- XXX how do I get the _gid of this new game?
    
                insert into pref_scores (uid, gid, money, quit)
                    values(_uid0, _gid, _money0, _quit0);
    
                insert into pref_scores (uid, gid, money, quit)
                    values(_uid1, _gid, _money1, _quit1);
    
                insert into pref_scores (uid, gid, money, quit)
                    values(_uid2, _gid, _money2, _quit2);
                end;
        $BODY$ language plpgsql;
    

    然后我需要一些帮助来加入第一张桌子中的first_name,女性,头像数据的分数 - 这样我就可以在网页的表格中显示最近7天玩过的游戏列表:

    01.12.2011  Alice $10        Bob $20 Charlie -$30  17 rounds
    01.12.2011  Alice $0 (quit)  Bob $20 Charlie -$20  5  rounds
    

    更新

    由于 mu太短的帮助我现在我的桌子上装满了数据,但仍然无法弄清楚如何列出玩家所执行的所有游戏 - 以及他的2个对手和他们的分数。

    我有一张包含所有比赛的表格:

    # select * from pref_games limit 5;
     gid | rounds |          finished
    -----+--------+----------------------------
      1 |     10 | 2011-10-26 14:10:35.46725
      2 |     12 | 2011-10-26 14:34:13.440868
      3 |     12 | 2011-10-26 14:34:39.279883
      4 |     14 | 2011-10-26 14:35:25.895376
      5 |     14 | 2011-10-26 14:36:56.765978
    

    然后我在这里参加了第3场比赛的所有3名球员(以及他们的分数):

    # select * from pref_scores where gid=3;
             uid           | gid | money | quit
    -----------------------+-----+-------+------
     OK515337846127        |   3 |   -37 | f
     OK40798070412         |   3 |   -75 | f
     MR2871175175044094219 |   3 |   112 | f
    

    这些都是玩家用uid = DE9411玩的游戏:

    # select * from pref_scores where id='DE9411';
      uid   | gid | money | quit
    --------+-----+-------+------
     DE9411 |  43 |    64 | f
     DE9411 | 159 |  -110 | f
     DE9411 | 224 |    66 | f
     DE9411 | 297 |   -36 | f
     DE9411 | 385 |    29 | f
     DE9411 | 479 |   -40 | f
     DE9411 | 631 |   -14 | f
     DE9411 | 699 |   352 | f
     DE9411 | 784 |   -15 | f
     DE9411 | 835 |   242 | f
    

    但是如何在上述结果集中列出其他2名球员及其得分?

4 个答案:

答案 0 :(得分:1)

您不希望serial中的pref_scores,只需int

create table pref_scores (
        uid varchar(32) references pref_users,
        gid int references pref_games,  /* XXX serial ok here? */
        money integer not null,
        quit boolean
);

您想使用INSERT ... RETURNING ... INTO

create or replace function pref_insert_scores(
    _uid0 varchar, _money0 integer, _quit0 boolean,
    _uid1 varchar, _money1 integer, _quit1 boolean,
    _uid2 varchar, _money2 integer, _quit2 boolean,
    _rounds integer) returns void as $BODY$
declare
    _gid int;
begin
    insert into pref_games (rounds) values (_rounds) returning gid into _gid;
    -- etc...

答案 1 :(得分:1)

此查询应该适用于使用当前架构获取所需的列表。然而,有这么多连接,性能可能不会那么大。由于您知道您总是有3个用户,因此根据此表的预期大小,您可能希望测试具有单个游戏表的性能,并将用户的信息去规范化。

select g.finished, u1.uid u1, s1.money m1, s1.quit q1,  u2.uid u2, s2.money m2, s2.quit
q2, u3.uid u3, s3.money m3, s3.quit q3, g.rounds
from pref_games g
inner join pref_scores s1 on s1.gid = g.gid 
inner join pref_scores s2 on s2.gid = g.gid and s2.uid > s1.uid
inner join pref_scores s3 on s3.gid = g.gid and s3.uid > s2.uid
inner join pref_users u1 on s1.uid = u1.uid
inner join pref_users u2 on s2.uid = u2.uid
inner join pref_users u3 on s3.uid = u3.uid
and g.finished > CURRENT_DATE - interval '1 week'

答案 2 :(得分:0)

在行中:

        finished timestamp default current_timestamp

我强烈建议使用

        finished timestamptz default current_timestamp

因为实际的日期/时间存储在GMT中,所以允许更加明智的日期/时间算术,因为实际的日期/时间存储在GMT中 - 它也更适合国际化。

从pg9.1.1手册'8.5。日期/时间类型':

[...]

注意:SQL标准要求只写时间戳等同于没有时区的时间戳,PostgreSQL会尊重这种行为。 (7.3之前的版本将其视为带时区的时间戳。)timestamptz被接受为带时区的时间戳的缩写;这是一个PostgreSQL扩展。

[...]

对于带时区的时间戳,内部存储的值始终为UTC(通用协调时间,传统上称为格林威治标准时间,GMT)。具有指定显式时区的输入值将使用该时区的适当偏移量转换为UTC。如果输入字符串中未指定时区,则假定它位于系统时区参数指示的时区中,并使用时区区域的偏移量转换为UTC。

当输出带有时区值的时间戳时,它始终从UTC转换为当前时区区域,并在该区域中显示为本地时间。要查看另一个时区的时间,请更改时区或使用AT TIME ZONE构造(参见第9.9.3节)。

[...]

答案 3 :(得分:0)

        gid serial references pref_games,  /* XXX serial ok here? */

这里你应该使用'int'而不是'serial' - 因为你需要在pref_games中引用一个非常具体的记录。