【问题标题】:SQL UPDATE WHERE CASESQL UPDATE WHERE CASE
【发布时间】:2020-10-07 23:02:16
【问题描述】:

我知道我的 SQL 知识很薄弱。不幸的是,我发现我知道的比我以前想象的要少。

我编写了一个UPDATE 命令,用于使用表单的简单 HTML 设置页面。 (在数据到达 UPDATE SQL 之前,有一些浏览器端验证和更多服务器端验证表单数据)。

我的目标是更新 2 行中的 1 列。没有其他的。但是我错了。它用预期的值更新了两个目标行。 SQL 还NULLed 所有其他行中所有值的列。

我的问题 1:是什么导致我的 SQL 出现这种情况? 我的问题 2:有没有更好的方法来执行此更新?

<cfquery datasource="OurDBLocation" name="changeValues">
    UPDATE sometable
    SET rowvalueB = CASE
    WHEN rowvalueA = 'A123' THEN #val(FORM.a123value)#
    WHEN rowvalueB = 'A456' THEN #val(FORM.a456value)#
    END
    OUTPUT INSERTED.*
    WHERE rowvalueA IN ('A123','A456')
</cfquery>

注意:这是一种内部表单,只有对我们的代码库有 VPN 访问权限的开发人员才能访问。对于不懂#val(FORM.a123value)#的人,我们在后端使用Lucee(又名ColdFusion)

【问题讨论】:

  • “SQL 还清空了所有其他行中所有值的列。” 上面只会更新 rowvalueA 的值的行'A123''A456'没有 其他人。但是请注意,在您的CASE 中,您首先检查rowvalueA,然后检查rowvalueB,因此除非rowvalueA rowvalueB 的值都为'A456',否则任何行其中rowvalueA 的值为'A456' 将更新为NULL,因为CASE 表达式中没有ELSE。我“闻到”印刷错误。 rowvalueA rowvalueB.
  • 对于#val(FORM.a123value)#等用户输入,您应该始终使用cfqueryparam
  • 在您的UPDATE 中,添加一个默认条件:CASE... WHEN .... THEN.... ELSE rowvalueB END。这将阻止该值更新为NULL。但我相信它也会像您的OUTPUT 中的INSERTED 记录一样,因为该行已更新。
  • @Larnu 谢谢。唯一的可能是我在测试中运行了一次上述语句,它不包括END 条件。排除END 条件是否会导致其余数据库记录为NULLed?
  • @RRK。谢谢你。如前所述(There is some browser side validation and also more server side validation of the form data before the data reaches the UPDATE SQL). 该公司不相信cfqueryparam。 IDK 为什么,他们就是不这样做。无论哪种方式,我都同意你的看法。

标签: sql tsql coldfusion lucee


【解决方案1】:

编辑:2020-10-07

对于最新的 cmets,如果您想减少 UPDATEs 的数量,尤其是当它没有更改值但仍导致写入时,您可以使用 CTE 仅过滤您需要更新的行,然后只使用这些。

<cfquery datasource="OurDBLocation" name="changeValues">
; WITH cte AS (
    SELECT rowvalueB
    FROM sometable
    WHERE rowvalueA = 'A123'
        OR ( rowvalueA = 'A456' AND rowvalueB = 'A456' )
)
UPDATE cte
SET rowvalueB =
        CASE
            WHEN rowvalueA = 'A123' THEN <cfqueryparam value="#val(FORM.a123value)#" cfsqltype="cf_sql_integer">
            ELSE <cfqueryparam value="#val(FORM.a456value)#" cfsqltype="cf_sql_integer">
        END
    OUTPUT INSERTED.*
;
</cfquery>

CTE UPDATE 非常棒,如果它们在您的 SQL 风格中可用(并且通过标记 tsql,我猜您是 SQL Server)。对其进行测试以确保它适用于您的情况。我不知道为什么 cfquery 不能正确运行 CTE UPDATE,但我现在无法对其进行测试。

解释正在发生的事情: ;WITH cte AS.. 创建命名的 CTE 数据集。另见https://www.sqlshack.com/sql-server-common-table-expressions-cte/ SELECT... 定义构成 CTE 的数据。由于您要过滤 rowvalueA IN (A123,A456) 但随后更新不同的值,因此我简化了 WHERE 语句。只有当 rowvalueBrowvalueA = 'A456' 时,您才会更新 FORM.a456 值。因此,CTE 过滤掉了 rowvalueA=A456rowvalueB 不是 A456 的所有行。这些将是最初重置为NULL 的记录。 然后使用普通的UPDATE 语句来更新CTE,它将通过管道传输到表中的实际行。由于您仍在更新中使用条件值,因此我们必须使用CASE...WHEN...,但行已被过滤和简化。我们只需要检查rowvalueA=A123.

另外请注意,当我提到客户端验证时,我并不是说不要这样做。我只是指出客户端验证很容易绕过,因此不应依赖于任何类型的验证,这些验证旨在用于客户端显示或易用性之外的任何内容。

========== 原始答案 =======================

如果我正确理解您的意图,您的查询似乎大部分是正确的。您只是在表中更新rowdataB。但是,如果您的 WHEN 条件不满足,则您不会给出默认值,因此会将 NULL 分配给它。

<cfquery datasource="OurDBLocation" name="changeValues">
    UPDATE sometable
    SET rowvalueB = 
        CASE
            WHEN rowvalueA = 'A123' THEN <cfqueryparam value="#val(FORM.a123value)#" cfsqltype="cf_sql_integer">
            WHEN rowvalueB = 'A456' THEN <cfqueryparam value="#val(FORM.a456value)#" cfsqltype="cf_sql_integer">
            ELSE rowvalueB
        END
    OUTPUT INSERTED.*
    WHERE rowvalueA IN ('A123','A456')
</cfquery>

如果您的CASE 中的任何WHEN 条件都不满足,这只会将rowvalueB 的值重新分配给它自己。由于WHERE 子句,它将忽略任何其他行。

认为如果rowvalueA'A456',这可能会将该行添加到您的OUTPUT INSERTED.*,因为它将在您的WHERE 子句中匹配,但在您的WHEN 子句中不匹配。如果这不是您想要的,您可能需要更改您的WHERE

【讨论】:

  • 注意:除了美化您的 UI 之外,不应依赖客户端验证。即使来自开发用户。可能特别是来自开发用户,他们可能具有高于平均水平的权限。在您将数据插入数据库或以任何其他方式使用它之前,请始终在您控制的某个地方(即服务器内部的某个地方)验证它。如上所述,利用cfqueryparam
  • 谢谢。正如我的问题所述:`(在数据到达 UPDATE SQL 之前,有一些浏览器端验证和更多服务器端验证表单数据)` 至于你的答案,似乎添加 ELSE rowvalueB 条件会产生不必要的写作到数据库。无论如何,我将来会考虑到这一点。
  • @davidhartman00 非常正确。但是,不包括它也会导致额外的写入,因为该值在没有任何条件的情况下被重写为 NULL。您可以修改WHERE 语句以过滤掉被重置为NULL 的行,或者更好的是,可能使用CTE,然后使用UPDATE CTE。我会更新我的答案以显示我在说什么。
猜你喜欢
  • 2018-01-26
  • 2012-03-12
  • 1970-01-01
  • 2020-01-25
  • 1970-01-01
  • 1970-01-01
  • 2011-03-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多