ruby on rails - Are you allowed to use a find_each query with an includes statement? -


example:

foobar.joins(:baz).includes(:baz).count => 22926 foobar.joins(:baz).includes(:baz).find_each.count => 998 foobar.joins(:baz).find_each.count => 22926 

the generated sql in correct case (third) several batches of sql looks like:

select  "foobar".* "foobar" inner join "baz" on  "baz"."foobar_id" = "foobar"."id" order "foobar"."id" asc limit $1 

in failing (second) case there single query looks like:

select  "foobar"."id" t0_r0  "baz"."id" t1_r0  "baz"."foobar_id" t1_r1  "foobar" inner join "baz" on "baz"."foobar_id" = "foobar"."id"   order "foobar"."id" asc limit $1 

where of fields listed different temporary variable (e.g. t0_r0) different columns on each table (in actual query there 37 split 30 on first object, 7 on second).

is bug? includes not allowed in find_each query? doing wrong?

the relationship between foobar , baz foobar has_one baz , baz belongs_to foobar.

this problem can happen if has_one relationship isn't has_one.

say database doesn't have unique index on column baz.foobar_id. accidentally end situation have foobar record connected more 1 baz record:

baz.id | baz.foobar_id ------   ------------- 1        1 2        1 3        2 

in situation, joins return combination of foobar , baz records:

foobar.joins(:baz).count  # 3 

this means find_each join iterate 3 times , repeat 1 of foobar ids:

foobar.joins(:baz).find_each(batch_size: 2) { |f| puts f.id } # select  "foobar".* "foobar" inner join "baz" on... limit 2 1 1 # select  "foobar".* "foobar" inner join "baz" on... ("foobar"."id" > 1) ... limit 2 2 

adding in includes means rails going try consolidate results set of distinct foobar records. won't work how find_each manages batches:

foobar.joins(:baz).includes(:baz).find_each(batch_size: 2) { |f| puts f.id } # select  "foobar"."id" t0_r0 ... limit 2 1 

and @ point find_each stop processing because has found batch smaller batch size, thinks done:

# activerecord::batches#in_batches break if ids.length < batch_limit 

the default batch size find_each 1,000. problem case returned 998 records. indicates first batch loaded 998 unique foobar ids less batch size, , find_each thought done. loaded 1,000 baz records connected 998 distinct foobar records.

you may want review baz table see if has duplicate entries. can like:

baz.group(:foobar_id).having('count(*) > 1') 

the best solution use unique index avoid duplicates in database , enforce has_one relationship. alternative ensure you're getting distinct set of foobar records like:

foobar.group(:id).joins(:baz).includes(:baz).count foobar.group(:id).joins(:baz).includes(:baz).find_each.count foobar.group(:id).joins(:baz).find_each.count 

Comments

Popular posts from this blog

Command prompt result in label. Python 2.7 -

javascript - How do I use URL parameters to change link href on page? -

amazon web services - AWS Route53 Trying To Get Site To Resolve To www -