简体   繁体   中英

Find only records which have all deleted associations

The question goes as RoR but I guess the question applies more on a query level.

I have a model

ModelA has_many ModelB

and I'm using paranoid gem in both of them.

I am trying to find out all the ModelA which have either no ModelB associated or all its ModelB have been deleted (therefore deleted_at is not NULL).

Based on this question , I was able to achieve the desired result, but reading the query it does not make much sense to me how it is working.

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)

As mentioned, reading this query it does not make sense to me because the joins condition I'm using is also being as null on the where clause afterwards.

Can someone please help me out getting the query properly set and if this is the right way to go, explain me how does the query breakdown into the right result.

Update:

As mentioned on a comment below, after some raw sql I managed to understand what was going on. Also, wrote my AR solution as to make it more clear (at least from my perspective)

Breaking it down:

This query represents a regular left outer join, which returns all models on the left independently if there is an association on the right.

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

Adding the extra condition in the join

AND model_bs.deleted_at IS NULL

will return the rows with ModelB attributes available if there is one associated, else will return one row with only the ModelA .

Applying over these rows the .where('model_bs.model_a_id IS NULL') will keep only rows that have no associated models.

Note: In this case, using the attribute model_a_id or any other attribute of model_b will work, but if going this way, I'd recommend using an attribute that if the association exists, its always going to be there. Otherwise you might end up with wrong results.

Finally, the AR that I've ended up by using to translate what I wanted:

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')

Hey you can try this way i have tested it in mysql you can verify it with 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')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM