【发布时间】:2011-12-16 22:01:57
【问题描述】:
I posted a related question,但这是我谜题的另一部分。
我想从已更新的行中获取列的 OLD 值 - 不使用触发器(也不使用存储过程,也不使用任何其他额外的非 SQL/查询实体)。
我有一个这样的查询:
UPDATE my_table
SET processing_by = our_id_info -- unique to this worker
WHERE trans_nbr IN (
SELECT trans_nbr
FROM my_table
GROUP BY trans_nbr
HAVING COUNT(trans_nbr) > 1
LIMIT our_limit_to_have_single_process_grab
)
RETURNING row_id;
如果我可以在子查询的末尾做FOR UPDATE ON my_table,那将是神圣的(并解决我的其他问题/问题)。但这行不通:不能将其与GROUP BY 结合使用(这是计算计数所必需的)。然后我可以只取那些 trans_nbr 并首先进行查询以获取(即将成为)以前的 processing_by 值。
我试过这样做:
UPDATE my_table
SET processing_by = our_id_info -- unique to this worker
FROM my_table old_my_table
JOIN (
SELECT trans_nbr
FROM my_table
GROUP BY trans_nbr
HAVING COUNT(trans_nbr) > 1
LIMIT our_limit_to_have_single_process_grab
) sub_my_table
ON old_my_table.trans_nbr = sub_my_table.trans_nbr
WHERE my_table.trans_nbr = sub_my_table.trans_nbr
AND my_table.processing_by = old_my_table.processing_by
RETURNING my_table.row_id, my_table.processing_by, old_my_table.processing_by
但这行不通; old_my_table 在连接之外不可见; RETURNING 子句对此视而不见。
我早已数不清自己做过的所有尝试;我已经研究了好几个小时了。
如果我能找到一种防弹的方法来锁定我的子查询中的行 - 并且只锁定那些行,并且当子查询发生时 - 我试图避免的所有并发问题都会消失......
更新:我在上面的非通用代码中有错字。在 Erwin Brandstetter 建议它应该可以工作后,我重试了。既然我花了这么长时间才找到这种解决方案,也许我的尴尬是值得的?至少这是为了后代现在...... :>
我现在拥有的(有效的)是这样的:
UPDATE my_table
SET processing_by = our_id_info -- unique to this worker
FROM my_table AS old_my_table
WHERE trans_nbr IN (
SELECT trans_nbr
FROM my_table
GROUP BY trans_nbr
HAVING COUNT(*) > 1
LIMIT our_limit_to_have_single_process_grab
)
AND my_table.row_id = old_my_table.row_id
RETURNING my_table.row_id, my_table.processing_by, old_my_table.processing_by AS old_processing_by
COUNT(*) 是根据 Flimzy 在对我的另一个(上面链接)问题的评论中提出的建议。
Please see my other question 用于正确实现并发甚至非阻塞版本;此查询仅显示如何从更新中获取旧值和新值,忽略错误/错误的并发位。
【问题讨论】:
-
为什么不能使用规则或触发器?
-
恕我直言,最好的方法是使旧行具有历史意义,无论是通过显式 SQL,还是通过重写规则或触发器。
-
@Flimzy: 1. 如果一个人无法访问这些东西(尽管 我 可以),如果它可以纯粹在 SQL/单个查询中完成...... 2. 规则/触发器是一个完整的“另一个调试辣酱玉米饼馅”。 3. 保持简单、直接的 SQL 并使用一个查询来完成所有操作很好。再次感谢 count(*) 提醒,寿! :>
-
@wildplasser:目标是找回 (1) 更改的内容和 (2) 更改之前的内容(至少)。这对于事先没有明确知道要更改的内容的作业很方便,但是程序应该知道旧的 val 用于处理什么。 (例如,在故障排除之前/之后输出。)历史行是不必要的混乱,规则和触发器不仅是杂乱无章的(对于这个用例),而且还需要“更多”(安全、访问等)。对于那些没有/想要/需要这些的人来说,这个解决方案是最好的。
标签: sql postgresql concurrency sql-update subquery