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=1
的count
递增,因为val1
和val2
的id=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=1
和 id=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 准备好的语句