我有一个包含多个列的表t
,我们将它们命名为a
,b
和c
。我还有一个state
列,表示当前状态。还有id
列。
我想编写以下查询:始终更新列a
,但仅当应用程序b
仍等于数据库时才c
和state
state
的。这里,state
列用于乐观锁定。
我写了这个查询如下:
UPDATE t
SET a = $a$,
b = (CASE WHEN state = $state$ THEN $b$ ELSE b END),
c = (CASE WHEN state = $state$ THEN $c$ ELSE c END)
WHERE id = $id$ AND
(
a != $a$ OR
b != (CASE WHEN state = $state$ THEN $b$ ELSE b END) OR
c != (CASE WHEN state = $state$ THEN $c$ ELSE c END)
)
此处,$id$
,$a$
,...是来自应用程序的输入变量。 WHERE
子句的第二部分是为了避免无法有效更新任何内容的更新。
此查询按预期工作,但非常笨拙。我几次重复同样的情况。我正在寻找一种以更优雅的方式重写此查询的方法。如果这是一个简单的SELECT
查询,我可以使用LATERAL JOIN
执行某些操作,但我看不到如何在此处应用此内容。
如何改进此查询?
答案 0 :(得分:1)
将查询拆分为两个:
UPDATE t
SET a = $a$
WHERE id = $id$
UPDATE t
SET b = $b$,
c = $c$
WHERE id = $id$ AND
state = $state$
如果你需要原子性,请包装一个事务。
答案 1 :(得分:1)
这似乎更清洁(未经测试):
WITH src AS (
SELECT $a$ AS a
, (CASE WHEN state = $state$ THEN $b$ ELSE b END) AS b
, (CASE WHEN state = $state$ THEN $c$ ELSE c END) AS c
FROM t
WHERE id = $id$
)
UPDATE t dst
SET a=src.a, b=src.b, c=src.c
FROM src
WHERE dst.id = src.id
AND (src.a, src.b, src.c) IS DISTINCT FROM (dst.a, dst.b, dst.c)
;
答案 2 :(得分:-1)
您需要的唯一过滤器是ID = $ id
案例陈述表示如果状态不匹配,请不要在更新中更改它,因此您不需要对其进行过滤。
修改
$updated->appendChild($client);
$updated->appendChild($clientID);
echo $xml->saveXML();
如果你做了更多,那么"总是更新"不一定是真的。
第3次尝试检查是否存在相同的可能性,但b或c更新。
答案 3 :(得分:-1)
编辑:我花了一些时间来实现我的错:这个问题显然是针对单个更新,而我的回答是尝试更新多行。但是,如果您需要为一组行执行此更新,您可以:
UPDATE t
SET a=source.a,
b=source.b,
c=source.c
FROM
(
SELECT
id,
a,
(CASE WHEN UpdateCondition THEN $b$ ELSE b END) AS b,
(CASE WHEN UpdateCondition THEN $c$ ELSE c END) AS c
FROM
(
SELECT state = $state$ As UpdateCondition, * FROM t
) As t2
WHERE
id = $id$ AND
(
a != $a$ OR
b != (CASE WHEN UpdateCondition THEN $b$ ELSE b END) OR
c != (CASE WHEN UpdateCondition THEN $c$ ELSE c END)
) AS source
WHERE t.id=source.id;
t2的子查询为您提供状态条件,并且每行只执行一次计算。
“source”的子查询为您提供映射值并过滤那些没有更改的值。