2016-09-13 4 views
-1

Вопрос идет как RoR, но я думаю, что этот вопрос больше применим к уровню запросов.Найти только записи, у которых есть все удаленные ассоциации

У меня есть модель

ModelA has_many ModelB 

и я использую paranoid камень в обоих из них.

Я пытаюсь выяснить все ModelA, которые либо не ModelB связаны или вообще не его ModelB были удалены (поэтому deleted_at не NULL).

Основываясь на this question, я смог добиться желаемого результата, но, читая запрос, мне не очень-то понятно, как он работает.

ModelA.joins('LEFT OUTER JOIN model_bs ON model_bs.model_a_id = model_as.id AND model_bs.deleted_at IS NULL') 
    .where('model_bs.model_a_id IS NULL') 
    .group(model_as.id) 

Как уже упоминалось, чтение этого запроса не имеет смысла для меня, потому что присоединяется к условию, я использую также быть утратившим на ИНЕКЕ впоследствии.

Может кто-то, пожалуйста, помогите мне правильно установить запрос, и если это правильный путь, объясните мне, как разбивка запроса на правильный результат.

Update:

Как уже упоминалось на комментарии ниже, после того, как какой-то сырой SQL мне удалось понять, что происходит. Кроме того, написал мое AR решение, чтобы сделать его более ясным (по крайней мере, с моей точки зрения)

разбив его:

Этот запрос представляет собой регулярное левое внешнее соединение, которое возвращает все модели на левой независимо если есть ассоциация справа.

ModelA.joins('LEFT OUTER JOIN model_bs ON model_bs.model_a_id = model_as.id') 

Добавление дополнительного условия в соединении

AND model_bs.deleted_at IS NULL

будет возвращать строки с ModelB атрибутов, доступных, если есть один, связанный, в противном случае будет возвращать одну строку с только ModelA.

Применение к этим строкам будет содержать только строки, не имеющие связанных моделей.

Примечание: В этом случае, используя атрибут model_a_id или любой другой атрибут model_b будет работать, но если идти по этому пути, я бы рекомендовал использовать атрибут, если связь существует, его всегда должны быть там. В противном случае у вас могут быть неправильные результаты.

Наконец, AR, что я в конечном итоге с помощью перевести то, что я хотел:

ModelA.joins('LEFT OUTER JOIN model_bs ON model_bs.model_a_id = model_as.id AND model_bs.deleted_at IS NULL') 
    .group('model_as.id') 
    .having('count(model_bs.id) = 0') 
+0

У меня была аналогичная проблема, вы можете проверить этот ответ: http://stackoverflow.com/a/15660222/580346 – mrzasa

+0

После публикации здесь я продолжал искать объяснения. Немного сырья sql вместо AR помогло мне понять причину. – rpbaltazar

ответ

0

Эй, вы можете попробовать этот путь я испытал его в mysql вы можете проверить его с PG

ModelA.joins('LEFT OUTER JOIN model_bs ON model_bs.model_a_id = model_as.id') 
    .group('model_as.id') 
    .having('SUM(CASE WHEN model_bs.deleted_at IS NULL THEN 0 ELSE 1 END) AS OCT = 0') 
+0

Пожалуйста, проверьте мой обновленный ответ, как я уже сам себе ответил. Почему я предпочитаю использовать обложенную сумму, когда могу просто добавить еще одно условие в соединениях? Это не похоже на путь. – rpbaltazar

+0

@rpbaltazar вы уверены, что ваше обновление работает согласно вашему требованию? я предполагаю, что он возвращает только modelA, не имеющий modelB, он не вернет вашу 'modelA', у которой все моделиB будут удалены. –

+0

Я уверен, что он работает. Использование соединений с предикатом 'AND model_bs.deleted_at IS NULL' дает мне это. Если есть какая-либо связь с 'deleted_at' NULL, она вернет строку для каждой ассоциации, иначе она вернет строку без ассоциации. Поэтому, когда я группируюсь по 'model_as.id',' model_bs.id' либо существует, либо больше или равно 1 или нет. – rpbaltazar

Смежные вопросы