PostgreSQL增加表中行的值

时间:2011-04-22 22:16:40

标签: php postgresql simultaneous

我目前正在PHP构建一个脚本,当它完成它的目的时必须更新统计信息。该脚本由Web浏览器访问,并且可以根据流量同时执行。我必须保证统计数据是正确的。

为了给你拍照,让我们说我们有一张桌子:

CREATE TABLE statistics(
  user_id      integer NOT NULL,
  date         integer NOT NULL, -- for unix time
  stat1        integer NOT NULL DEFAULT 0,
  stat2        integer NOT NULL DEFAULT 0,
  stat3        integer NOT NULL DEFAULT 0  -- and so on...
);

-- Let's insert some testing data for a couple of users and days...
-- Day one
INSERT INTO statistics(1, 1303520820, 1, 1, 1);
INSERT INTO statistics(2, 1303520820, 1, 1, 1);
-- Day two
INSERT INTO statistics(1, 1303603200, 1, 1, 1);
INSERT INTO statistics(2, 1303603200, 1, 1, 1);
-- Day three
INSERT INTO statistics(1, 1303689600, 1, 1, 1);
INSERT INTO statistics(2, 1303689600, 1, 1, 1);

每天在表格中插入一个新行,这样我们就可以获得每日,每周,每月,每年的统计数据。我必须确保每天 user_id 只插入一行。此外,每当执行UPDATE查询时,它都会适当地增加列 stat1 stat2 stat3

这个脚本应该有相当多的流量,我想弄清楚如何在执行脚本时让事情工作并且有几个实例同时工作。您最适合这些任务的方法/技术是什么?

3 个答案:

答案 0 :(得分:3)

最简单的解决方案是添加唯一约束

CREATE TABLE statistics(
  user_id      integer NOT NULL,
  date         integer NOT NULL, -- for unix time
  stat1        integer NOT NULL DEFAULT 0,
  stat2        integer NOT NULL DEFAULT 0,
  stat3        integer NOT NULL DEFAULT 0,  -- and so on...
  UNIQUE(user_id,date)
);

无论你采取什么其他措施,你都应该这样做。

答案 1 :(得分:1)

正如其他人所说,你需要对user_id和date这一对的唯一约束。

为了在复合键(user_id,date)不存在时不进行算术运算,并且当复合键 存在时用算术更新,则需要编写一些代码。非正式地,这被称为“upsert”。不止一种方式。

PosgreSQL docs有一个函数示例,它使用异常处理来实现这种要求。函数的问题在于,您无法强制应用程序代码或数据库女孩每次都使用它,毫无例外。

你可以(我认为)使用suppress_redundant_updates_trigger()。触发器的优点是它们不能被应用程序代码或数据库女孩意外绕过。我自己没有使用过这种技术,所以我无法对此进一步评论。此触发器记录在here

您也可以handle the upsert logic with a user-defined trigger

答案 2 :(得分:1)

此外,您可以添加CHECK日期值以确保它是1天的倍数:

ALTER TABLE "statistics" ADD CONSTRAINT "1day_quantum" CHECK ("date" = ("date" / 86400)::INTEGER * 86400);

如果尝试插入错误的日期值,则会抛出异常。

如果日期字段类型为TIMESTAMP或TIMESTAMPTZ,则CHECK更复杂:

ALTER TABLE "statistics" ADD CONSTRAINT "1day_quantum" CHECK ("date" = TIMESTAMP 'epoch' + ((EXTRACT(EPOCH FROM "date") / 86400)::INTEGER * 86400) * INTERVAL '1 second');

通过更改86400(秒计数),您可以将约束调整为各种量程:900例如15分钟。