Руководство по использованию предложения WITH в SQL
Я понимаю, как использовать WITH
предложение для рекурсивных запросов (!!), но у меня возникли проблемы с пониманием его общего использования / мощности.
например, следующий запрос обновляет одну запись, идентификатор которой определяется с помощью подзапроса, возвращающего идентификатор первой записи по метке времени:
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
обертка вместо относительно уродливого подзапроса? Если так, то почему?
2 ответов
Если может быть одновременный доступ на запись к задействованным таблицам, условия гонки в вышеуказанных следующих запросах. Подумайте:
ваш пример можете используйте 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
запросы на изменение данных с помощью CTE возможны с PostgreSQL 9.1 или более поздней версии.
Читать больше в превосходном руководстве.
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.перспектива, чей статус "новый" или "сброс".
отметьте его, добавив звездочку к его статусу
верните строку (включая нашу настройку в
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
или добавьте выражение volatile.