【问题标题】:Update A Record if it exists, else do nothing, including short-cicruit如果存在则更新 A 记录,否则不执行任何操作,包括短路
【发布时间】:2021-02-26 10:45:36
【问题描述】:

我想做的是具有以下逻辑的东西:

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时才更新?

到目前为止,我最好的解决方案是使用公用表表达式(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;

甚至这都不起作用(仍然在 UPDATE 行报告语法错误):

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

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

选择它可以工作,所以我完全不理解它为我的更新查询返回的语法错误 #1064:

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 和 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 '='

【问题讨论】:

  • 您必须为整个查询提供示例数据,以便社区能够为您提供帮助。
  • 从更新关键字开始,查询完全按照原样,请关注该部分/如何使其工作

标签: sql sql-update mariadb common-table-expression mariadb-10.3


【解决方案1】:

明白了,以下查询完全符合我在 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 秒,而不会对表做任何事情。如果确实退出,则执行需要 0.0014 秒,同时相应地更新目标记录。因此,如果在 target_table 中找不到目标记录,它似乎确实可以工作并节省资源。非常感谢所有提供帮助的人!

【讨论】:

    【解决方案2】:

    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 有用吗?一秒钟我会检查..
    • 我不知道。 mysql 的update ... from ... 语法可能略有不同。
    • 不,不工作;它说'FROM(SELECT ...)'上的语法错误;这太令人沮丧了,好像 mariadb 缺少一些功能。您推荐的最佳 RDBMS 是哪个?而且,您是否有其他与 mariadb 兼容的解决方案? (另外,请注意我的 RDBMS 是 mariadb,而不是 mysql,它们在可用功能方面确实不同..)
    【解决方案3】:

    只需执行UPDATE。如果ID 没有行,它将什么也不做。这可能不会比先测试慢。

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

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

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

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

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

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

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

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

    【讨论】:

    • 是的,不需要iodku,因为我不想插入任何东西,但如果记录不存在,什么也不做。我只是害怕在目标记录不存在的所有情况下进行不必要的查询(两个查询和一些计算来计算更新值)。有没有办法只在以下情况下进行查询、计算和更新记录不存在?还是查询首先在哪里处理和短路?你明白我的意思吗?
    • @DevelJoe - 是的。只是给你一个你明天可能需要的小费。
    • 我添加了更多讨论。
    • 非常感谢,但我仍然无法正常工作,它仍然说 UPDATE 行有语法错误..
    • 我们看看语法错误;它指向有问题的令牌。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-10
    • 1970-01-01
    • 1970-01-01
    • 2021-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多