PostgreSQL:为每一行运行查询并将结果保存在其中

时间:2011-06-21 13:42:42

标签: postgresql cron

我将每周游戏分数存储在一个名为 pref_money 的表格中:

# select * from pref_money limit 5;
       id       | money |   yw
----------------+-------+---------
 OK32378280203  |   -27 | 2011-44
 OK274037315447 |   -56 | 2011-44
 OK19644992852  |     8 | 2011-44
 OK21807961329  |   114 | 2011-44
 FB1845091917   |   774 | 2011-44
(5 rows)

对于每周的获奖者,我会展示奖章:

screenshot

我通过运行来找到用户的奖牌数量:

# select count(id) from (
     select id,
            row_number() over(partition by yw order by money desc) as ranking
     from pref_money
) x
where x.ranking = 1 and id='OK260246921082';
 count
-------
     3
(1 row)

这个查询非常昂贵:

# explain analyze select count(id) from (
    select id,
           row_number() over(partition by yw order by money desc) as ranking
    from pref_money
) x
where x.ranking = 1 and id='OK260246921082';
                                                                QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=18946.46..18946.47 rows=1 width=82) (actual time=2423.145..2423.145 rows=1 loops=1)
   ->  Subquery Scan x  (cost=14829.44..18946.45 rows=3 width=82) (actual time=2400.004..2423.138 rows=3 loops=1)
         Filter: ((x.ranking = 1) AND ((x.id)::text = 'OK260246921082'::text))
         ->  WindowAgg  (cost=14829.44..17182.02 rows=117629 width=26) (actual time=2289.079..2403.685 rows=116825 loops=1)
               ->  Sort  (cost=14829.44..15123.51 rows=117629 width=26) (actual time=2289.069..2319.575 rows=116825 loops=1)
                     Sort Key: pref_money.yw, pref_money.money
                     Sort Method:  external sort  Disk: 4320kB
                     ->  Seq Scan on pref_money  (cost=0.00..2105.29 rows=117629 width=26) (actual time=0.006..22.566 rows=116825 loops=1)
 Total runtime: 2425.001 ms
(9 rows)

这就是为什么(因为我的网站在高峰时期挣扎,在pgbouncer日志中显示50个查询/秒)我想缓存该值并添加了一列奖牌到另一个表 - pref_users

pref=> \d pref_users;
                Table "public.pref_users"
   Column   |            Type             |   Modifiers
------------+-----------------------------+---------------
 id         | character varying(32)       | not null
 first_name | character varying(32)       |
 last_name  | character varying(32)       |
 female     | boolean                     |
 avatar     | character varying(128)      |
 city       | character varying(32)       |
 lat        | real                        |
 lng        | real                        |
 login      | timestamp without time zone | default now()
 last_ip    | inet                        |
 medals     | smallint                    | default 0
 logout     | timestamp without time zone |
Indexes:
    "pref_users_pkey" PRIMARY KEY, btree (id)
Check constraints:
    "pref_users_lat_check" CHECK ((-90)::double precision <= lat AND lat <= 90::double precision)
    "pref_users_lng_check" CHECK ((-90)::double precision <= lng AND lng <= 90::double precision)
    "pref_users_medals_check" CHECK (medals >= 0)

我想创建一个每15分钟运行一次的cronjob,以便为 pref_users 表中的所有用户更新该列:

*/15       *       *       *       *       psql -a -f $HOME/bin/medals.sql

如你所见,我几乎掌握了一切。我的问题是我还没有提出用于更新奖牌列的SQL语句。

请帮忙吗?

我正在使用PostgreSQL 8.4.8和CentOS Linux 5.6 / 64位。

谢谢! 亚历

1 个答案:

答案 0 :(得分:1)

那么,这不会产生用户ID和奖牌数的结果吗?

create view user_medal_count as
select id, count(*) as medals from (
     select id,
            row_number() over(partition by yw order by money desc) as ranking
     from pref_money
) x
where x.ranking = 1
group by id

因此,您需要将其用作更新用户的来源:

update pref_users
set medals = user_medal_count.medals
from user_medal_count
where pref_users.id = user_medal_count.id
      and (pref_users.medal_count is null
           or pref_users.medal_count <> user_medal_count.medal_count)

我希望能让你开始。

还有一些问题需要考虑。您可能想要定义用户获得奖牌的时间点 - “当前周”的奖牌可能会发生变化,因此您可能希望将奖牌数量定义为稳定计数前几周的奖牌,即时计算当前一周的奖牌(这应该要求查看更少的数据),或者只是排除它。 (如果你没有做任何事情,那么你可能会发现如果用户暂时获得当前一周的奖牌,他们会获得1的medal_count,但如果稍后将其重新授予其他人,则永远不会重置为0。