Я сумасшедший: оператор PostgreSQL IN с вложенным запросом возвращает неожиданные результаты - PullRequest
6 голосов
/ 12 сентября 2011

Следующий запрос возвращает 2036 строк:

SELECT "FooUID" from "Foo" f
LEFT JOIN "Bar" b ON f."BarUID" = b."BarUID"
WHERE f."BarUID" IS NOT NULL AND b."BarUID" IS NULL

Но следующий оператор обновил только 1870 строк:

UPDATE "Foo" f1 set "BarUID" = 'aNewUID'
WHERE f1."FooUID" IN (
   SELECT f2."FooUID" from "Foo" f2
   LEFT JOIN "Bar" b ON f2."BarUID" = b."BarUID"
   WHERE f2."BarUID" IS NOT NULL AND b."BarUID" IS NULL
)

Как это возможно?

РЕДАКТ. 1: Первый запрос продолжает возвращать 166 строк, а второй продолжает обновлять 0 строк.

РЕДАКТИРОВАТЬ 2:

В дальнейшем вложенный запрос возвращает строку, содержащую UID, но внешний запрос возвращает 0 строк.

SELECT * from "Foo" f1
WHERE f1."FooUID" = (
   SELECT f2."FooUID" FROM "Foo" f2
   LEFT JOIN "Bar" b ON f2."BarUID" = b."BarUID"
   WHERE f2."BarUID" IS NOT NULL AND b."BarUID" IS NULL
   LIMIT 1
)

Я сумасшедший?

РЕДАКТИРОВАТЬ 3:

Следующий оператор, предоставленный @ wildplasser , обновил оставшиеся 166 строк:

UPDATE "Foo" ff
SET "BarUID" = 'aNewUID'
WHERE ff."BarUID" IS NOT NULL
AND NOT EXISTS (
   SELECT * FROM "Bar" bb
   WHERE bb."BarUID"= ff."BarUID"
)

Однако я до сих пор не понимаю, почему оригинал их не взял. Если во вложенном запросе выбрано 166 "FooUID" с, почему бы не сопоставить их со строками таблицы "Foo", используя IN?

РЕДАКТИРОВАТЬ 4: Чем больше я думаю об этом, этот фон может быть важным:

Все это происходило на сервере базы данных, который был недавно клонирован с другого. Я поговорил с ИТ-специалистом, который выполнял клонирование, и выяснилось, что он не закрывал приложение, работающее поверх исходной БД, перед тем, как его клонировать. Это означает, что БД, скорее всего, была сбита в середине транзакции (я не знаю, насколько неблагодарно). Возможно ли, что что-то в базе данных было повреждено, что заставило меня увидеть эти фантомные строки?

К сожалению, я больше не могу его воспроизвести, так как запустил исправление wildplasser. В исходной БД (снова запущенной и обслуживающей приложение) нет ни одной недопустимой информации, которую я пытался исправить в копии, и тем более никаких следов махинаций, свидетелем которых я был.

Я должен упомянуть, что перед запуском исправления я довел проблему до самого простого абсурда: сначала я выбрал FooUID из вложенного запроса в Edit 2, скопировал его в буфер обмена, затем запустил запрос, выбирая из Foo, где FooUID равно вставленному значению - это still вернуло 0 строк.

1 Ответ

2 голосов
/ 12 сентября 2011

Что произойдет, если переписать это с NOT EXIST, например

UPDATE Foo ff
SET baruid = 'aNewUID'
WHERE ff.baruid IS NOT NULL
AND NOT EXISTS (SELECT * FROM bar bb
    WHERE bb.baruid = ff.baruid
    );

Выглядит для меня намного чище, чем выбор конечности внешнего соединения.

...