Sql 在UPSERT的更新部分,为多个列计算一次表达式
我有一个Sql 在UPSERT的更新部分,为多个列计算一次表达式,sql,postgresql,upsert,Sql,Postgresql,Upsert,我有一个插入。。。在发生冲突时(主键),在PostgreSQL 12数据库中执行更新查询 它要求我对update子句中的多个列执行相同的计算。计算不太复杂,但我不希望在两个地方保留它。计算包含对冲突行和EXCLUDED虚拟表的引用 是否有任何方法可以执行此计算一次并将其结果分配给单个查询表达式中的临时变量 完整查询是: INSERT INTO table_name(id, refreshed_at, tokens) VALUES ('id', CURRENT_TIMESTAMP, 60)
插入。。。在发生冲突时(主键),在PostgreSQL 12数据库中执行更新
查询
它要求我对update子句中的多个列执行相同的计算。计算不太复杂,但我不希望在两个地方保留它。计算包含对冲突行和EXCLUDED
虚拟表的引用
是否有任何方法可以执行此计算一次并将其结果分配给单个查询表达式中的临时变量
完整查询是:
INSERT INTO table_name(id, refreshed_at, tokens) VALUES ('id', CURRENT_TIMESTAMP, 60)
ON CONFLICT (id) DO UPDATE SET
tokens = GREATEST(
-1,
LEAST(
GREATEST(0, table_name.tokens)
+ EXTRACT(epoch FROM EXCLUDED.refreshed_at)
- EXTRACT(epoch FROM table_name.refreshed_at),
100
) - 1
),
refreshed_at = CASE
WHEN (
GREATEST(0, table_name.tokens)
+ EXTRACT(epoch FROM EXCLUDED.refreshed_at)
- EXTRACT(epoch FROM table_name.refreshed_at)
) > 0 THEN EXCLUDED.refreshed_at
ELSE table_name.refreshed_at
END
RETURNING tokens >= 0;
tokens
列是根据该列的前一个值计算的,即查询时间与上次更新该列时间之间的差值(以秒为单位)。只有更新了标记
列的值时,才应更改刷新列
,因此必须在两个集合
子句中执行相同的计算
仅当更新了令牌列值时,才应更改刷新的\u at列
您最初的想法应该与子查询一起使用:
INSERT INTO table_name(id, refreshed_at, tokens)
VALUES ('id', CURRENT_TIMESTAMP, 60)
ON CONFLICT (id) DO UPDATE
SET (tokens, refreshed_at)
= (SELECT GREATEST(-1, LEAST(GREATEST(0, table_name.tokens) + sub.diff, 100) - 1)
, CASE WHEN sub.diff > 0 THEN EXCLUDED.refreshed_at ELSE table_name.refreshed_at END
FROM (SELECT EXTRACT(epoch FROM EXCLUDED.refreshed_at - table_name.refreshed_at)::int) sub(diff))
RETURNING tokens >= 0;
而不是像原始图像那样提取两次并进行减法:
EXTRACT(epoch FROM EXCLUDED.refreshed_at) - EXTRACT(epoch FROM table_name.refreshed_at)
但是,添加子查询的开销可能比进行两次廉价计算要昂贵
替代解决方案
考虑将WHERE
子句添加到UPDATE
部分。比如:
INSERT INTO table_name(id, refreshed_at, tokens)
VALUES ('id', CURRENT_TIMESTAMP, 60)
ON CONFLICT (id) DO UPDATE
SET tokens = GREATEST(-1, LEAST(GREATEST(0, table_name.tokens) + EXTRACT(epoch FROM EXCLUDED.refreshed_at - table_name.refreshed_at), 100) - 1)
, refreshed_at = EXCLUDED.refreshed_at
WHERE EXTRACT(epoch FROM EXCLUDED.refreshed_at - table_name.refreshed_at)::int > 0
RETURNING tokens >= 0;
INSERT-INTO-table\u-name(id、刷新的\u-at、令牌)
值('id',当前时间戳,60)
在冲突(id)上执行更新
SET tokens=magest(-1,LEAST(magest(0,table_name.tokens)+EXTRACT(epoch FROM EXCLUDED.refreshed_at-table_name.refreshed_at),100)-1)
,refreshed_at=EXCLUDED.refreshed_at
其中提取(epoch FROM EXCLUDED.refreshed\u at-table\u name.refreshed\u at)::int>0
返回标记>=0代码>
这根本不会触及时差低于1秒的行。这是典型的优势,因为空更新只会增加成本和膨胀。(在施法回合中,您可能需要截断?)
当然,这也不会返回INSERT
和UPDATE
部分都没有通过的行。如果这是一个问题,请考虑:
只需显示语句(即使它不起作用)和您的Postgres版本。如果计算包含冲突行,您将如何重用一次计算的结果?“也许函数更有意义?”ErwinBrandstetter补充道。查询工作正常,但我不希望重复它,以防以后需要更新。括号似乎不匹配。另外:refreshed\u at
?refreshed\u at
的数据类型是时间戳,tokens
是整数,id
是文本。在粘贴查询之前,我对其进行了轻微的编辑,因此parens可能已关闭。
INSERT INTO table_name(id, refreshed_at, tokens)
VALUES ('id', CURRENT_TIMESTAMP, 60)
ON CONFLICT (id) DO UPDATE
SET tokens = GREATEST(-1, LEAST(GREATEST(0, table_name.tokens) + EXTRACT(epoch FROM EXCLUDED.refreshed_at - table_name.refreshed_at), 100) - 1)
, refreshed_at = EXCLUDED.refreshed_at
WHERE EXTRACT(epoch FROM EXCLUDED.refreshed_at - table_name.refreshed_at)::int > 0
RETURNING tokens >= 0;