我理解如何使用WITH
子句进行递归查询(!!),但是我在理解它的一般用途/能力时遇到了问题。
例如,以下查询更新一条记录,其id是通过使用子查询确定的,该子查询按时间戳返回第一条记录的id:
update global.prospect psp
set status=status||'*'
where psp.psp_id=(
select p2.psp_id
from global.prospect p2
where p2.status='new' or p2.status='reset'
order by p2.request_ts
limit 1 )
returning psp.*;
这是否适合使用WITH
包装而不是相对丑陋的子查询?如果是这样,为什么?
答案 0 :(得分:19)
如果可以对涉及的表进行并发写访问,则上述查询中存在竞争条件。考虑:
您的示例可以使用CTE(公用表表达式),但它不会为您提供任何子查询无法执行的操作:
WITH x AS (
SELECT psp_id
FROM global.prospect
WHERE status IN ('new', 'reset')
ORDER BY request_ts
LIMIT 1
)
UPDATE global.prospect psp
SET status = status || '*'
FROM x
WHERE psp.psp_id = x.psp_id
RETURNING psp.*;
顺便说一句,返回的行将是更新的版本。
如果您希望将返回的行插入另一个表,那么WITH子句就变为必不可少了:
WITH x AS (
SELECT psp_id
FROM global.prospect
WHERE status IN ('new', 'reset')
ORDER BY request_ts
LIMIT 1
), y AS (
UPDATE global.prospect psp
SET status = status || '*'
FROM x
WHERE psp.psp_id = x.psp_id
RETURNING psp.*
)
INSERT INTO z
SELECT *
FROM y
使用PostgreSQL 9.1或更高版本可以使用CTE进行数据修改查询 阅读more in the excellent manual。
答案 1 :(得分:10)
WITH
允许您定义“临时表”以在SELECT
查询中使用。例如,我最近编写了一个这样的查询来计算两组之间的变化:
-- Let o be the set of old things, and n be the set of new things.
WITH o AS (SELECT * FROM things(OLD)),
n AS (SELECT * FROM things(NEW))
-- Select both the set of things whose value changed,
-- and the set of things in the old set but not in the new set.
SELECT o.key, n.value
FROM o
LEFT JOIN n ON o.key = n.key
WHERE o.value IS DISTINCT FROM n.value
UNION ALL
-- Select the set of things in the new set but not in the old set.
SELECT n.key, n.value
FROM o
RIGHT JOIN n ON o.key = n.key
WHERE o.key IS NULL;
通过在顶部定义“表格”o
和n
,我可以避免重复表达式things(OLD)
和things(NEW)
。
当然,我们可以使用UNION ALL
删除FULL JOIN
,但在我的特定情况下我无法做到这一点。
如果我正确理解您的查询,则执行以下操作:
查找global.prospect中状态为“new”或“reset”的最旧行。
通过在状态
返回行(包括我们的调整到status
)。
我认为WITH
不会简化您案件中的任何内容。但是,使用FROM
子句可能稍微优雅一些:
update global.prospect psp
set status = status || '*'
from ( select psp_id
from global.prospect
where status = 'new' or status = 'reset'
order by request_ts
limit 1
) p2
where psp.psp_id = p2.psp_id
returning psp.*;
<子>未测试。如果有效,请告诉我。
这几乎就是你已经拥有的,除了:
这可以很容易地扩展到更新多行。在使用子查询表达式的版本中,如果更改子查询以产生多行,则查询将失败。
我在子查询中没有别名global.prospect
,所以它更容易阅读。由于这使用FROM
子句,如果您不小心引用了正在更新的表,则会出错。
在您的版本中,每个项目都会遇到子查询表达式。虽然PostgreSQL应该对此进行优化并且仅对表达式进行一次评估,但如果您不小心引用psp
中的列或添加易失性表达式,则此优化将消失。