Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 在UPSERT的更新部分,为多个列计算一次表达式_Sql_Postgresql_Upsert - Fatal编程技术网

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;