我正在建立各种排队机制.有需要处理的数据行和状态标志.我正在使用一个update .. returning条款来管理它:
UPDATE stuff
SET computed = 'working'
WHERE id = (SELECT id from STUFF WHERE computed IS NULL LIMIT 1)
RETURNING *
Run Code Online (Sandbox Code Playgroud)
嵌套的选择部分是否与更新锁相同,或者我是否有竞争条件?如果是这样,内部选择需要是select for update吗?
postgresql concurrency multithreading race-condition transaction-isolation
我正在使用PostgreSQL 9.3.
我想复制一些db记录.由于我正在为表使用自动增量pk id,因此我想从生成的重复记录ID返回到原始ID的id映射.例如,假设我有一个posts包含2条记录的表:
[{'id': 1, 'title': 'first'}
, {'id': 2. 'title': 'second'}]
Run Code Online (Sandbox Code Playgroud)
使用SQL:
INSERT INTO posts (title) SELECT title FROM posts RETURNING id, ??
Run Code Online (Sandbox Code Playgroud)
我希望看到像这样的映射:
[{'id': 3, 'from_id': 1}
, {'id': 4, 'from_id': 2}]
Run Code Online (Sandbox Code Playgroud)
有关如何填写上述问号以使其有效的任何想法?非常感谢!
使用Postgres,我可以执行更新语句并返回受推荐影响的行.
UPDATE accounts
SET status = merge_accounts.status,
field1 = merge_accounts.field1,
field2 = merge_accounts.field2,
etc.
FROM merge_accounts WHERE merge_accounts.uid =accounts.uid
RETURNING accounts.*
Run Code Online (Sandbox Code Playgroud)
这将为我提供与该WHERE子句匹配的所有记录的列表,但是不会告诉我操作实际更新了哪些行.
在这个简化的用例中,当然简单地添加另一个防护是微不足道的AND status != 'Closed,但是我的真实世界用例涉及从10,000个行的合并表中更新潜在的几十个字段,我希望能够检测哪些行实际上已经改变了,它们与之前的版本相同.(期望很少的行实际上会改变).
到目前为止我得到的最好的是
UPDATE accounts
SET x=..., y=...
FROM accounts as old WHERE old.uid = accounts.uid
FROM merge_accounts WHERE merge_accounts.uid = accounts.uid
RETURNING accounts, old
Run Code Online (Sandbox Code Playgroud)
这将返回一个新旧行的元组,然后可以在我的Java代码库本身内部进行区分 - 但这需要大量额外的网络流量并且可能容易出错.
理想的情况是能够让postgres返回实际有任何值更改的行 - 这可能吗?
在github上这是一个更真实的例子,我正在做的事情,结合到目前为止的一些建议.
使用Postgres 9.1,但如果需要可以使用9.4.要求是有效的
由于这个问题已经打开,我现在已经完成了大部分工作,虽然我不确定我的方法是否是一个好主意 - 它有点被黑客攻击.
我正在从 UUID 获取用户数据WHERE empl_user_pub_uuid = 'e2bb39f1f28011eab66c63cb4d9c7a34'。
由于我不想进行额外的查询来获取额外的用户数据,所以我试图通过INSERT.
WITH _u AS (
SELECT
eu.empl_user_pvt_uuid,
ee.email,
ep.name_first
FROM employees.users eu
LEFT JOIN (
SELECT DISTINCT ON (ee.empl_user_pvt_uuid)
ee.empl_user_pvt_uuid,
ee.email
FROM employees.emails ee
ORDER BY ee.empl_user_pvt_uuid, ee.t DESC
) ee ON eu.empl_user_pvt_uuid = ee.empl_user_pvt_uuid
LEFT JOIN (
SELECT DISTINCT ON (ep.empl_user_pvt_uuid)
ep.empl_user_pvt_uuid,
ep.name_first
FROM employees.profiles ep
) ep ON eu.empl_user_pvt_uuid = ep.empl_user_pvt_uuid
WHERE empl_user_pub_uuid = 'e2bb39f1f28011eab66c63cb4d9c7a34'
)
INSERT INTO employees.password_resets (empl_pwd_reset_uuid, empl_user_pvt_uuid, t_valid, for_empl_user_pvt_uuid, token)
SELECT 'f70a0346-a077-11eb-bd1a-aaaaaaaaaaaa', '6efc2b7a-f27e-11ea-b66c-de1c405de048', '2021-04-18 …Run Code Online (Sandbox Code Playgroud) sql postgresql greatest-n-per-group sql-insert sql-returning
我需要一个查询来更新表中的一行,但如果 id 不存在,它会插入默认值。它还必须避免线程竞争条件。
我在这里找到了一个应该没问题的答案 /sf/answers/554957021/
使用此查询:
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)
所以我认为它应该在更新后返回旧值,并且应该防止线程竞争条件。
但是,如果该行不存在,我需要添加一个插入,并且这次还返回插入的值(旧值没有意义,因为它们不存在)。
所以基本上我需要做类似的事情
INSERT INTO tbl
(...) VALUES (...)
RETURNING ..., ...
ON CONFLICT DO
UPDATE tbl x
SET tbl_id = 24
, name = 'New Gal'
FROM (SELECT tbl_id, name FROM tbl WHERE tbl_id = …Run Code Online (Sandbox Code Playgroud) sql postgresql insert-update race-condition select-for-update
UPDATE "Users"
SET "displayName" = 'new_name'
WHERE id = 1
RETURNING "displayName"
Run Code Online (Sandbox Code Playgroud)
上面的查询返回NEW值.我想知道更新后的OLD值displayName.我知道有可能:
UPDATE ...
FROM SELECT ...
RETURNING ...
Run Code Online (Sandbox Code Playgroud)
但我想使用AFTER UPDATE触发器.是否有可能以AFTER UPDATE某种方式从触发器"返回"旧值?如果是这样的触发器体如何?
我是编写DB函数的新手,我需要OUT在执行UPDATE查询时返回'last_login_at'的值作为参数.
这是我的功能片段:
...
LOOP
UPDATE "user" SET
last_login_at = current_timestamp,
first_name = p_first_name,
last_name = p_last_name,
WHERE ext_user_id = p_ext_user_id AND platform_id = p_platform_id
RETURNING id INTO v_user_id;
is_new := false;
// The next 'CASE' is not valid - Need to replace it with a valid one.
has_logged_in_today = CASE
WHEN date_part('day', age(current_timestamp, last_login_at)) > 1
THEN true
ELSE false
END;
IF FOUND THEN
EXIT;
END IF;
..
..
END LOOP;
Run Code Online (Sandbox Code Playgroud)
可以做多个RETURNING x INTO y …