PostgreSQL Upsert 用于几乎相似的值

Posted

技术标签:

【中文标题】PostgreSQL Upsert 用于几乎相似的值【英文标题】:PostgreSQL Upsert For Almost Similar Values 【发布时间】:2021-05-03 21:40:54 【问题描述】:

我正在尝试将相似值 (val1,val2) 的命中计数保持在指定阈值(±某个值)内。 对于完全匹配,我会使用 UPSERT,但我不知道如何使它适用于类似的匹配。

给定一张桌子valuetable

id val1 val2 count
1 1.1 2.2 2
2 1.7 2.2 1
3 1.0 2.2 1

我们应该这样做

INSERT INTO valuetable 
VALUES(DEFAULT, 1.2, 2.1, 1) 
ON CONFLICT (val1 ± 0.1,val2 ± 0.1)
DO 
  UPDATE SET count = count + 1

我们将得到下表,其中id=1count 递增,因为val1val2id=1 在指定的0.1 范围内。

id val1 val2 count
1 1.1 2.2 3
2 1.7 2.2 1
3 1.0 2.2 1

如果可能的话,还有另一个问题。 如果我们要进行下面的查询,id=1id=3 都符合条件。

INSERT INTO valuetable 
VALUES(DEFAULT, 1.0, 2.1, 1) 
ON CONFLICT (val1 ± 0.1,val2 ± 0.1)
DO 
  UPDATE SET count = count + 1

我们如何更新具有最高(或最低)count 的那个(例如,id=1 得到更新,因为它在所有匹配行中具有最高的 count,共 2 个)?

id val1 val2 count
1 1.1 2.2 3
2 1.7 2.2 1
3 1.0 2.2 1

更新净差最小(或最大)的那个(例如 id=3 得到更新,因为它的净差为 0.1 而不是 id=1 0.2)?

id val1 val2 count
1 1.1 2.2 2
2 1.7 2.2 1
3 1.0 2.2 2

注意:有过标准化值的想法,例如0.1, 0.2, ...,因此可以进行精确匹配, 但最好不要诉诸于此,因为0.1, 0.2, ... 的阈值为0.5 仍然会遇到同样的问题,而0.5, 1.0, ... 会丢失太多上下文。

【问题讨论】:

【参考方案1】:

您需要使用显式逻辑来执行此操作,而不是 upsert。我可能会建议一个两步流程:

with v as (
      select t.*
      from (values (1.2, 2.1)) v(val1, val2)
     ),
     i as (
      insert into valuetable (val1, val2, count)
          select val1, val2, 0
          from v
          where not exists (select 1
                            from valuetable vt
                            where abs(vt.val1 - v.val1) < 0.1 and
                                  abs(vt.val2 - v.val2) < 0.1
                           )
          returning *
     )
update valuetable vt
    set count = count + 1
    from v
    where abs(vt.val1 - v.val1) < 0.1 and
          abs(vt.val2 - v.val2) < 0.1;

这会插入计数为“1”的值,然后增加匹配行的值。这不是最有效的方法,但解释起来很简单。

另一种方法是对数据进行规范化,使值沿每个维度以 0.2 为增量 - 并为此创建一个唯一维度。然后你可以使用 upsert:

create unique index unq_t_val1_val2
    on (floor(val1 * 5), floor(val2 * 5));

然后你可以在你的问题中使用upsert。但是,不要在(val1, val2) 上单独创建唯一索引,因为两者会发生冲突。

【讨论】:

谢谢,决定是选择一个舒适的增量,但很巧合。

以上是关于PostgreSQL Upsert 用于几乎相似的值的主要内容,如果未能解决你的问题,请参考以下文章

使用 jOOQ 在 PostgreSQL 中进行 UPSERT

如何在 PostgreSQL 中进行 UPSERT(合并、插入……重复更新)?

将 Oracle upsert 转换为 PostgreSQL 准备好的语句

PostgreSQL INSERT ON CONFLICT UPDATE (upsert) 使用所有排除值

sql upsert 语句 - postgresql

SQLAlchemy - 在 postgresql 中执行批量 upsert(如果存在,更新,否则插入)