Выполняется только одно из двух UPDATE в CTE?

У меня возникла ситуация, когда мне приходится использовать CTE в этой форме.

WITH
update_field_a AS (
update super_table SET
  a = ....
WHERE condition to update a hold true
RETURNING TRUE
),
update_field_b AS (
update super_table SET
  b = ....
WHERE condition to update b hold true
RETURNING TRUE
)
SELECT 
  (SELECT true FROM update_field_a) as updated_a,
  (SELECT true FROM update_field_b) as updated_b,

Таким образом, в основном два CTE обновляют разные поля одной и той же таблицы, каждый из которых имеет свое собственное условие обновления в предложении WHERE.

Проблема в том, что поведение, которое я наблюдаю, заключается в том, что только CTE запускается в зависимости от того, какой из SELECT я вызываю первым в последнем запросе SELECT.

Это учитывая это

SELECT 
  (SELECT true FROM update_field_a) as updated_a, <-- first select
  (SELECT true FROM update_field_b) as updated_b,

update_field_a CTE будет запускаться и обновлять таблицу, в то время как update_field_b не запускается, поскольку (SELECT true FROM update_field_a) as updated_a вызывается первым.

Но если у меня есть:

SELECT 
  (SELECT true FROM update_field_b) as updated_b, <-- first select
  (SELECT true FROM update_field_a) as updated_a,

update_field_b CTE будет запускаться и обновлять таблицу, в то время как update_field_a не запускается, поскольку (SELECT true FROM update_field_a) as updated_a вызывается первым.

Как я могу обеспечить работу обоих CTE? Я думал, что ссылка на них в инструкции SELECT вызовет их оба, но по какой-то странной причине запускается только первая ссылка, которая получает ссылку.

Обратите внимание, что этот запрос является частью приложения Rust, использующего библиотеку sqlx. Обновление: я вижу такое же поведение, если запрос выполняется непосредственно в терминале psql, поэтому детали Rust/Sqlx не имеют никакого влияния.

Как вы можете быть уверены, что действительно запустилось только одно из обновлений? Почему вы помещаете оператор обновления в CTE? Почему бы просто не запустить два обновления напрямую, возможно, в рамках одной транзакции?

Tim Biegeleisen 05.09.2024 05:00

Я уверен, потому что проверяю таблицу в базе данных после выполнения запроса. И, к сожалению, в этом случае мне нужно сделать все за один запрос, поэтому его нельзя разделить на два. Обратите внимание, что приведенный выше пример представляет собой упрощенную версию реального запроса, поэтому существуют другие ограничения, которые заставляют это делать в одном запросе с CTE.

Finlay Weber 05.09.2024 05:02

Из какого инструмента вы выполняете Postgres SQL? Вы делаете это из администратора pg, из кода или где-то еще?

Tim Biegeleisen 05.09.2024 05:03

Фактически из кода. Растет с помощью sqlx. Я обновлю вопрос с этими подробностями

Finlay Weber 05.09.2024 05:07

Я создал упрощенную версию запроса и выполнил его непосредственно в psql и получил то же самое поведение.

Finlay Weber 05.09.2024 05:16

Вы упомянули, что включите свой код Rust... но этого еще не произошло.

Tim Biegeleisen 05.09.2024 05:18

Почему бы не использовать два простых оператора UPDATE в одной транзакции? Или одно ОБНОВЛЕНИЕ с условием CASE о том, что обновлять? Это значительно упростит задачу, и ваша проблема исчезнет.

Frank Heikens 05.09.2024 05:20

Подтверждено, что такое же поведение наблюдается, когда запрос выполняется непосредственно в psql, поэтому код Rust не имеет значения.

Finlay Weber 05.09.2024 05:20

@FrankHeikens ОП вызывает Postgres из кода приложения Rust. Поэтому я попросил показать код, чтобы подсказать, как использовать одну транзакцию.

Tim Biegeleisen 05.09.2024 05:21

@FrankHeikens есть и другие ограничения, согласно которым это следует делать в одном запросе с использованием CTE. Кроме того, это не помогает объяснить странное поведение, которое я наблюдаю, потому что теоретически имеющиеся у меня CTE должны работать нормально, странно, что только один из них, который запрашивается первым, запускается, а другой игнорируется. Я хотел бы знать, почему это так.

Finlay Weber 05.09.2024 05:22

> Поэтому я попросил показать код, чтобы я мог предложить, как использовать одну транзакцию @FrankHeikens, которая не имеет смысла :( и отвечает на совершенно другой вопрос. Вопрос не в том, как разделить этот CTE на транзакции, а в том, как объяснение того, почему у меня есть CTE, который теоретически должен работать, но демонстрирует поведение, которое тот, который работает, зависит от того, на который ссылаются первым в последнем операторе SELECT, - и как исправить CTE, чтобы не делать этого

Finlay Weber 05.09.2024 05:26
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
11
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Все CTE и основной запрос видят один и тот же снимок базовых таблиц. Обновление одной и той же строки несколько раз не работает.

Как я могу обеспечить работу обоих CTE?

Вы не можете. Либо разделите запрос на два отдельных запроса, либо объедините оба обновления в одно. Нравиться:

UPDATE super_table
SET    a = CASE WHEN condition_a THEN ... ELSE a END
     , b = CASE WHEN condition_b THEN ... ELSE b END
WHERE (condition_a OR condition_b)
RETURNING condition_a AS updated_a
        , condition_b AS updated_b;

Связанный:

> Все CTE и основной запрос видят один и тот же снимок базовых таблиц. Обновление одной и той же строки несколько раз не работает. - Но это работает для первого обновления. Итак, я все еще пытаюсь понять, как тот факт, что все они видят один и тот же снимок, гарантирует, что первый, на который ссылается последний выбор, фактически обновляет снимок.

Finlay Weber 05.09.2024 05:36

@FinlayWeber: Это всего лишь деталь реализации, и она ненадежна. CTE, хотя и перечислены последовательно, на самом деле не имеют порядка. Они могут выполняться в любом произвольном порядке — если только вы не объедините их с помощью предложения RETURNING. Тогда первый элемент SELECT первым активирует CTE. Другой не может снова обновить ту же строку. Но именно так решает планировщик запросов. Во второй добавленной мной ссылке обсуждается аналогичный случай.

Erwin Brandstetter 05.09.2024 05:40

Другие вопросы по теме