【问题标题】:PostgreSQL increment values of rows in a tablePostgreSQL 增加表中行的值
【发布时间】:2011-04-22 22:16:40
【问题描述】:

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

为了给你图片,假设我们有一张桌子:

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 查询时,它都会适当地增加列 stat1stat2stat3

这个脚本预计会有相当多的流量,我想弄清楚当脚本执行并且有几个实例同时工作时如何使事情正常工作。您认为哪种方法/技术最适合此类任务?

【问题讨论】:

  • 每天一行还是每个 user_id 每天一行?无论您想保证哪一个,primary keyunique 都是您的朋友。
  • 每天每个 user_id 一行。我会改正的 - 很抱歉这个错误:)

标签: php postgresql simultaneous


【解决方案1】:

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

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)
);

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

【讨论】:

  • 是的,我想。很可能我只会将 UNIQUE 添加到日期列。我无法将它添加到 user_id 列,因为该表可以有无数行具有相同的 user_id :)
  • 第二个虽然我不能添加唯一约束到结构。请参阅问题中的示例。 user_id 可以相同,日期也可以相同。每个用户每天都会在数据库中获得一条新记录。
  • UNIQUE(user_id,date) 表示 user_id 和 date 的每个组合必须是唯一的,它与 UNIQUE(date),UNIQUE(user_id) 不同,即每个用户每个日期只能有 1 行,而不是每个用户 1 行用户和每个日期 1 行。
  • 我认为 OP 也需要一个触发器。触发器将确定是插入还是更新行,并实现算术。
  • @catcall,我会使用一个过程而不是触发器
【解决方案2】:

正如其他人所说,您需要对 user_id 和 date 对进行唯一约束。

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

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

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

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

【讨论】:

    【解决方案3】:

    您还可以为日期值添加 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 分钟。

    【讨论】:

      猜你喜欢
      • 2020-04-02
      • 2018-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-21
      • 1970-01-01
      • 2013-01-13
      相关资源
      最近更新 更多