pyt*_*rry 42 sql postgresql subquery sql-update
我有一个相关的问题,但这是我的谜题的另一部分.
我想从UPDATEd的行中获取列的OLD VALUE - 不使用触发器(也不使用存储过程,也不使用任何其他额外的非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;
Run Code Online (Sandbox Code Playgroud)
如果我可以在子查询结束时执行"FOR UPDATE ON my_table",那将是devine(并修复我的其他问题/问题).但是,这不起作用:不能有这个AND"GROUP BY"(这对于计算trans_nbr的COUNT是必要的).然后我可以直接使用那些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
Run Code Online (Sandbox Code Playgroud)
但这不起作用; old_my_table在联接之外是不可见的; 该RETURNING条款对此视而不见.
我早就忘记了我所做的所有尝试; 我一直在研究这个问题.
如果我能找到一种防弹的方法来锁定子查询中的行 - 而且只有那些行,并且当子查询发生时 - 我试图避免的所有并发问题都会消失......
更新: [WIPES EGG OFF FACE]好的,所以我在上面的非通用代码中写了一个拼写错误,我写的"不起作用"; 它确实......感谢下面的Erwin Brandstetter,他说过会这样做,我重新做了这件事(经过一夜的睡眠,精神焕发的眼睛,以及香槟的bfast).既然我花了很长时间/很难找到这种解决方案,也许我的尴尬值得吗?至少现在这是为了子孙后代......:>
我现在拥有的(有效)是这样的:
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
Run Code Online (Sandbox Code Playgroud)
该COUNT(*)是从每一个建议Flimzy在我的其他(以上链接)的问题中留言.(我比必要的更具体.[在这种情况下.])
请参阅我的另一个问题,以正确实现并发,甚至是非阻塞版本; 此查询仅显示如何从更新中获取旧值和新值,忽略错误/错误并发位.
Erw*_*ter 65
optional
RETURNING子句导致UPDATE根据实际更新的每一行计算和返回值.FROM可以计算使用表的列和/或其中提到的其他表的列的任何表达式.使用表的列的新(更新后)值.RETURNING列表的语法与输出列表的语法相同SELECT.
强调我的.无法访问RETURNING子句中的旧行.你可以做,在触发或者单独SELECT 之前的UPDATE,包裹在一个事务中@Flimzy和@wildplasser评论,或包裹在CTE为@MattDiPasquale公布.
但是,如果您在子句中加入表的另一个实例,那么您尝试实现的功能完全正常FROM:
UPDATE tbl x
SET tbl_id = 23
, name = 'New Guy'
FROM tbl y -- using the FROM clause
WHERE x.tbl_id = y.tbl_id -- must be UNIQUE NOT NULL
AND x.tbl_id = 3
RETURNING y.tbl_id AS old_id, y.name AS old_name
, x.tbl_id , x.name;
Run Code Online (Sandbox Code Playgroud)
返回:
old_id | old_name | tbl_id | name
--------+----------+--------+---------
3 | Old Guy | 23 | New Guy
Run Code Online (Sandbox Code Playgroud)
我用PostgreSQL版本从8.4到9.6进行了测试.
它有所不同INSERT:
有几种方法可以避免并发写操作的竞争条件.简单,缓慢且可靠(但价格昂贵)的方法是使用SERIALIZABLE隔离级别运行事务.
BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE ..;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
但这可能是矫枉过正的.如果序列化失败,您需要准备好重复操作.
简单和快速(和公正的并发写入负载可靠)是在明确的锁定一个行进行更新:
UPDATE tbl x
SET tbl_id = 24
, name = 'New Gal'
FROM (SELECT tbl_id, name FROM tbl WHERE tbl_id = 4 FOR UPDATE) y
WHERE x.tbl_id = y.tbl_id
RETURNING y.tbl_id AS old_id, y.name AS old_name, x.tbl_id, x.name;
Run Code Online (Sandbox Code Playgroud)
此相关问题下的更多解释,示例和链接:
您可以使用SELECT子查询.
示例:更新用户的电子邮件RETURNING旧值.
RETURNING 子查询
UPDATE users SET email = 'new@gmail.com' WHERE id = 1
RETURNING (SELECT email FROM users WHERE id = 1);
Run Code Online (Sandbox Code Playgroud)WITH u AS (
SELECT email FROM users WHERE id = 1
)
UPDATE users SET email = 'new@gmail.com' WHERE id = 1
RETURNING (SELECT email FROM u);
Run Code Online (Sandbox Code Playgroud)
这对我没有失败的本地数据库工作了好几次,但我不知道,如果SELECT在WITH保证前的一贯执行UPDATE,因为"与子语句彼此之间以及与主查询并发执行."
@MattDiPasquale提出的CTE变体也应该起作用.
有了CTE的舒适手段,我会更明确,但是:
WITH sel AS (
SELECT tbl_id, name FROM tbl WHERE tbl_id = 3 -- assuming unique tbl_id
)
, upd AS (
UPDATE tbl SET name = 'New Guy' WHERE tbl_id = 3
RETURNING tbl_id, name
)
SELECT s.tbl_id AS old_id, s.name As old_name
, u.tbl_id, u.name
FROM sel s, upd u;
Run Code Online (Sandbox Code Playgroud)
在没有测试的情况下,我宣称这是有效的:SELECT并UPDATE查看数据库的相同快照.该SELECT势必返回旧值(即使您将CTE的CTE与后UPDATE),而UPDATE收益率的定义的新值.瞧.
但它会慢于我的第一个答案.
| 归档时间: |
|
| 查看次数: |
23812 次 |
| 最近记录: |