Выделите строки, которые не совпадают в объединяемой таблице с условием где - PullRequest
0 голосов
/ 23 января 2020

В приложении Rails с Postgres у меня есть таблица пользователей, вакансий и подписчиков. Я хочу выбрать задания, за которыми не следует указанный пользователь c. Но также и задания без строк в объединяемой таблице.

Таблицы:

users: 
  id: bigint (pk)

jobs:
  id: bigint (pk)

followings:
  id: bigint (pk)
  job_id: bigint (fk)
  user_id: bigint (fk)

Данные:

sandbox_development=# SELECT id FROM jobs;
 id 
----
  1
  2
  3
(3 rows)
sandbox_development=# SELECT id FROM users;
 id 
----
  1
  2
sandbox_development=# 
SELECT id, user_id, job_id FROM followings;
 id | user_id | job_id 
----+---------+--------
  1 |       1 |      1
  2 |       2 |      2
(2 rows)

Ожидаемый результат

# jobs
 id 
----
  2
  3
(2 rows)

Могу ли я создать запрос на присоединение, эквивалентный этому?

sandbox_development=# 
SELECT j.id FROM jobs j 
WHERE NOT EXISTS(
  SELECT 1 FROM followings f 
  WHERE f.user_id = 1 AND f.job_id = j.id 
);

 id 
----
  2
  3
(2 rows)

Какая работа, но PITA для создания с ActiveRecord.

Пока что я иметь:

Job.joins(:followings).where(followings: { user_id: 1 })
SELECT "jobs".* FROM "jobs" 
INNER JOIN "followings" 
ON "followings"."job_id" = "jobs"."id" 
WHERE "followings"."user_id" != 1

Но поскольку это внутреннее соединение, оно не включает задания без подписчиков (идентификатор задания 3). Я также пробовал различные попытки внешних объединений, которые либо дают все строки, либо не содержат строк.

Ответы [ 2 ]

3 голосов
/ 23 января 2020

В Rails 5 вы можете использовать #left_outer_joins там, где не добиться результата. Левые соединения не возвращают нулевые строки. Итак, нам нужно добавить ноль условий для извлечения строк.

Rails 5 Запрос:

Job.left_outer_joins(:followings).where.not(followings: {user_id: 1}).or(Job.left_outer_joins(:followings).where(followings: {user_id: nil}))

Альтернативный запрос:

Job.left_outer_joins(:followings).where("followings.user_id != 1 OR followings.user_id is NULL")

Postgres Запрос:

SELECT "jobs".* FROM "jobs" LEFT OUTER JOIN "followings" ON "followings"."job_id" = "jobs"."id" WHERE "followings"."user_id" != 1 OR followings.user_id is NULL;
1 голос
/ 23 января 2020

Я не уверен, что понимаю, но у вас есть выход, который вы хотите, и используйте внешнее соединение:

SELECT j.* 
FROM jobs j LEFT JOIN followings f ON f.job_id = j.id
LEFT JOIN users u ON u.id = f.user_id AND u.id = 1 
WHERE u.id IS NULL;
...