如果存在则更新 A Record,否则什么也不做,包括短路

Update A Record if it exists, else do nothing, including short-cicruit

我想做的是具有以下逻辑的事情:

IF EXISTS (SELECT * FROM people WHERE ID = 168)
THEN UPDATE people SET calculated_value = complex_queries_and_calculations
WHERE ID = 168

..,因此如果给定记录包含给定数据,则更新给定记录的字段,否则什么也不做。要生成将用于更新的数据,我需要查询其他表的值并进行一些计算。如果实际上没有什么可更新的话,我想避免这些查询+计算。在这种情况下,什么都不做。因此,我猜想将例如 EXIST 子句放在 UPDATE 语句的 WHERE 子句中将导致许多查询和计算失败。

我怎样才能只有条件地更新而不做任何事情,并确保仅在需要更新时才进行计算用于更新的值所需的所有查询和计算?然后,最后,只有在 complex_queries_and_calculations 不为 NULL 时才进行更新?

到目前为止,我最好的解决方案是使用通用 Table 表达式(WITH 子句),这使得无法短路。无论如何,为了让您能够理解我正在努力实现的逻辑,我展示了到目前为止我一直在尝试的内容(没有成功;下面的代码不起作用,我不知道为什么..):

-- complex queries and calculations; return result as t.result
WITH t AS(complex queries and calculations)
UPDATE target_table
SET
CASE
WHEN t.result IS NOT NULL
THEN target_table.target_column = t.result WHERE target_table.PK = 180
END;

更新(仍然说语法错误,仍然不工作)

WITH t AS(complex_queries_and_calculations AS stamp)
UPDATE target_table
SET target_column =
CASE
WHEN t.stamp IS NULL
THEN target_column
ELSE t.stamp
END
WHERE ID = 168;

即使这样也行不通(仍在更新行上报告语法错误):

WITH t AS(complex_queries_and_calculations AS stamp)
UPDATE target_table
SET target_column = target_column
WHERE ID = 168;

(避免冗余的最终更好的方法 target_column = target_column 欢迎更新)

使用 select 它有效,所以我完全不理解语法错误 #1064 它 returns 我的更新查询:

WITH t AS(complex_queries_and_calculations AS stamp)
SELECT
CASE
WHEN t.stamp IS NULL
THEN "Error!"
ELSE t.stamp
END
FROM t;

附加信息

MariaDB 似乎实际上不支持 CTEs with UPDATE 语句;如果我错了请纠正我...所以我尝试了以下操作:

UPDATE people AS p
INNER JOIN (queries_and_calculations AS result) t
ON p.ID <> t.result -- just to join
SET p.target_column = t.result
WHERE p.ID = 168
AND t.result IS NOT NULL;

现在它说:

#4078 - Illegal parameter data types varchar and row for operation '='

只需执行 UPDATE。如果没有包含 ID 的行,它将不执行任何操作。这可能不会比先测试慢。

同上 DELETE 当该行可能不存在时。

"Upsert"/"IODKU" -- INSERT ... ON DUPLICATE KEY UPDATE ... 当你想在行存在时修改某些列(根据一些唯一列)或添加新行(当它不存在时)时很有用存在)。 这个比先做一个SELECT好。

这样想...UPDATE的很大一部分是

  • 打开 table,
  • 找到table中需要修改的块
  • 正在将该块加载到缓存中(“buffer_pool”)

您的 SELECTUPDATE 都需要所有这些(是的,多余的)。 UPDATE 继续:

  • 如果该行不存在,则退出。
  • 修改行,并将块标记为“脏”。
  • 在后台,块最终会被刷新到磁盘。

(我省略了有关事务完整性(“ACID”)等的详细信息)

即使在最坏的情况下,整个任务(对于单行)也需要不到 10 毫秒。在最好的情况下,它需要不到 1 毫秒,并且可以与某些其他活动并行完成。

SQL 中没有 IF,因为不需要:


UPDATE people p
SET calculated_value = c.val
FROM (
        SELECT ID, val
        FROM
        ... complex_queries_and_calculations
        ) c
WHERE c.ID = p.ID
AND ID = 168
AND v.val <> i.val -- maybe add this to avoid idempotent updates. Beware of NULLs, though!
        ;

~

知道了,下面的查询完全符合我在 Mariadb 中的要求:

UPDATE target_table

LEFT JOIN (complex_queries_and_calculations_to_get_update_value AS update_value) t

ON target_table.ID <> t.update_value -- serves just to have update value in memory, 
-- because it needs to be accessed twice to create the updated column value 
-- on update, sort of a workaround for CTE + UPDATE in MariaDB

SET target_column = JSON_ARRAY( FORMAT_UPDATE_VALUE(t.update_value),
                    FORMAT_2_UPDATE_VALUE(t.update_value) )

WHERE ID = 128 AND t.update_value IS NOT NULL;

如果记录不存在,查询将花费大约 0.0006 秒来执行,而不对 table 做任何事情。如果确实退出,则执行需要 0.0014 秒,同时相应地更新目标记录。因此,如果在 target_table 中找不到目标记录,它似乎确实有效并且可以节省资源。非常感谢所有提供帮助的人!