仅包含具有更新值的列的动态更新语句

Posted

技术标签:

【中文标题】仅包含具有更新值的列的动态更新语句【英文标题】:Dynamic Update Statement with only the columns that have updated values 【发布时间】:2021-11-23 03:27:17 【问题描述】:

我需要执行更新语句,并且在发送 http 补丁请求之前我不确定要更新的列。我需要修改员工记录,并且需要构造一个仅包含已更新值的列的更新语句。

该表有列employeerecords(ID,name,dateOfBirth,startyear,endyear,company)。 以及以下示例响应:


"name":"Jim"

更新:发送补丁请求时,我通过 URI 参数提供 ID。我也在Mule 4中实现。我不知道如何完成以下。

更新员工记录 SET -未知- WHERE ID =:ID;

【问题讨论】:

一般来说,应用程序应该始终向后端提供所有的字段值——完整的记录——而不仅仅是更改的值。在您的示例中,数据甚至不包括记录的关键字段:您怎么会知道要更新的 哪个 记录,更不用说要设置什么值了...这不是好的设计。 @pmdba,我在上面提供了更多信息。我通过 URI 参数提供 PK。 这仍然不是一个好的设计。 API 应接受所有参数 - 完整记录 - 作为输入并发出单个预先确定的更新语句。或者,不同的 API 调用可以接受特定的字段子集作为输入,但 SQL 仍然应该是静态的,而不是动态的。为 DML 命令构建动态 SQL 是 SQL 注入或其他问题的秘诀。 我在这里同意@pmdba。从技术上讲,您可以在收到请求后测试每一列,然后从那里构建一个动态 sql,其中包含您要更新的列。 【参考方案1】:

您实际上并不需要使用动态 SQL 来解决此问题。假设您使用的是支持 JSON_TABLE 的 Oracle 版本,则以下查询就足够了:

UPDATE employee e
SET (name, dateofbirth, startyear, endyear, company) =
    (SELECT NVL(j.name, e.name), NVL(j.dateofBirth, e.dateofBirth),
            NVL(j.startyear, e.startyear), NVL(j.endyear, e.endyear),
            NVL(j.company, e.company)
     FROM JSON_TABLE('"name":"Jim"' --REPLACE WITH ACTUAL REQUEST
                      COLUMNS(name, dateOfBirth DATE, startyear, endyear, company)) j)
WHERE e.id = 1; --REPLACE WITH ACTUAL ID

我最好将绑定变量用作过程的一部分,而不是对 JSON 和 ID 进行硬编码。

我创建了一个 DBFiddle 来显示查询的有效性:(Link)

编辑 1:添加 NULL CHECKING

正如 cmets 中所指出的,如果用户将 NULL 传递给请求中的值,我的原始查询将不起作用。我已修改我的查询以使用EXISTS 功能:

UPDATE employee e
SET (name, dateofbirth, startyear, endyear, company) =
    (SELECT DECODE(j.name_chk, 1, j.name, e.name), 
            DECODE(j.dob_chk, 1, j.dateofbirth, e.dateofBirth),
            DECODE(j.start_chk, 1, j.startyear, e.startyear),
            DECODE(j.end_chk, 1, j.endyear, e.endyear),
            DECODE(j.co_chk, 1, j.company, e.company)
     FROM JSON_TABLE('"name":"Jim","endyear":null' 
                      COLUMNS(name, name_chk NUMBER EXISTS PATH '$.name', 
                              dateOfBirth DATE, dob_chk NUMBER EXISTS PATH '$.dateofbirth',
                              startyear, start_chk NUMBER EXISTS PATH '$.startyear',
                              endyear, end_chk NUMBER EXISTS PATH '$.endyear',
                              company, co_chk NUMBER EXISTS PATH '$.company')) j)
WHERE e.id = 1;

我也更新了 DBFiddle (Link)

【讨论】:

如果调用 API 的用户想要将其中一列设置为空值,则此方法将不起作用。例如,请求为"name":"Jim","endyear":null 的主体不会将endyear 列设置为空,它会保留旧值。 @EJEgyed 感谢您的评论。你完全正确。我添加了一些功能来检查明确的NULL

以上是关于仅包含具有更新值的列的动态更新语句的主要内容,如果未能解决你的问题,请参考以下文章

具有可变列名的动态更新语句

MySQL更新列的序列号按具有相同值的字段分组

使用窗口函数更新具有多个值的列

使用动态更新查询更新 Ledger_stat 表中的天数列的值

计算具有相同 id 的列的值的存储过程

SQL :具有动态列值分配的更新语句