Trying to get a list of households with one person in them

in my models, household has_many :people and people has_one :household
household has a name field, and people has household_id

the closest I got was:
@test = Household.count :all, :group => 'people.household_id', :joins
=> :people

this does group the families together and count the members,
but this has no way of including only families of one
I am assuming that the output from this could be used to get the name
field from households

in my models, household has_many :people and people has_one :household
household has a name field, and people has household_id

That should be person belongs_to household.

the closest I got was:
@test = Household.count :all, :group => 'people.household_id', :joins
=> :people

this does group the families together and count the members,
but this has no way of including only families of one
I am assuming that the output from this could be used to get the name
field from households

It might be worth looking at counter_cache.

Colin

That might not be a bad idea (or even running a Household.connection.select_value on the SQL).

Well, the answer is going to be different for ActiveRecord 2.x and 3.0

Here's how it would look in SQL

SELECT COUNT(households.id) FROM households
INNER JOIN people ON people.household_id = households.id
GROUP BY households.id
HAVING COUNT(people.id) = 1

In AR 2.x, that's probably:

Household.count(:include

As Colin (sort of) suggested, your Household model should contain

has_many :people

and your Person model should contain

belongs_to :household

Don't forget the colons, as shown above.

Next, since the foregoing relationship will allow you to determine the
number of People in each Household, I would stay away from trying to
create an SQL statement, and, instead, allow the controller to provide
the appropriate array to your view, as follows:

def single_person_household
  households = Household.all
  @households_with_one = Array.new
  for h in households do
    if h.people.count == 1.to_i
      @households_with_one << h
    end
  end
end

NOTE: If you create a view called single_person_household.html.erb,
then you will need a route that maps to that view in your routes.rb
file, such as

map.connect 'single_person_household', :controller =>
'households', :action => 'single_person_household'

In your view, you will use @households_with_one.

By the way, before I post code, I test it, so I know that I am
actually answering the question which I understood to have been asked.

Sandy

in my models, household has_many :people and people
has_one :household
household has a name field, and people has household_id

That should be person belongs_to household.

the closest I got was:
@test = Household.count :all, :group => 'people.household_id', :joins
=> :people

this does group the families together and count the members,
but this has no way of including only families of one
I am assuming that the output from this could be used to get the name
field from households

It might be worth looking at counter_cache.

Colin

That might not be a bad idea (or even running a
Household.connection.select_value on the SQL).

Well, the answer is going to be different for ActiveRecord 2.x and 3.0

Here's how it would look in SQL

SELECT COUNT(households.id) FROM households
INNER JOIN people ON people.household_id = households.id
GROUP BY households.id
HAVING COUNT(people.id) = 1

In AR 2.x, that's probably:

Household
.count
(:include

>
:people
, :group=>'households.id', :having=>'COUNT(people.id)=1').first.first

or since the join is simple and not truly needed:
Person.count(:select => 'people.id', :group =>
'people.household_id', :having => 'COUNT(id)=1').first.first

(the return will be an array of pairs [count,1] so [[count,
1]].first.first will be count)

And in AR 3.0, something like:

Household
.select
('COUNT
(households
.id
)').includes
(:people).group('households.id').having('COUNT(people.id)=1').to_a.size
-or-
Person
.select
('COUNT(id)').group('household_id').having('COUNT(id)=1').to_a.size

You might be thinking "Hmm, ActiveRecord doesn't seem to be well
suited for a query like this"

And I think you'd be right!

-Rob

Rob Biedenharn
R...@AgileConsultingLLC.com http://AgileConsultingLLC.com/
r...@GaslightSoftware.com http://GaslightSoftware.com/- Hide quoted text -

- Show quoted text -

As Colin (sort of) suggested, your Household model should contain

has_many :people

and your Person model should contain

belongs_to :household

Don't forget the colons, as shown above.

Next, since the foregoing relationship will allow you to determine the
number of People in each Household, I would stay away from trying to
create an SQL statement, and, instead, allow the controller to provide
the appropriate array to your view, as follows:

def single_person_household
households = Household.all
@households_with_one = Array.new
for h in households do
   if h.people.count == 1.to_i
     @households_with_one << h
   end
end
end

But since Bob hasn't indicated the size of the Household and Person data sets, I'd caution that performance of that code on a large number of households could be dismal as it has to first read every household into an object (i.e., memory) and then make a separate query to the database to count the persons.

Also, there's never a need to do 1.to_i (1 is already an integer).

If you really wanted to add this as Ruby code (rather than trying to leverage ActiveRecord directly, I'd suggest something at least a bit more idiomatic if no more efficient.

class Person < ActiveRecord::Base
   belongs_to :household
end

class Household < ActiveRecord::Base
   has_many :people

   # reads all records, then a COUNT(*) query to determine people
   def self.single_person_households
     find(:all).select {|household| household.people.count == 1 }
   end

   # Or since the original question was for a count,
   # not the actual records...
   # Get just the COUNT of single person households
   def self.count_single_person_households
     connection.select_value(<<-ENDSQL)
       SELECT COUNT(households.id) AS the_count
       FROM households
       INNER JOIN people ON people.household_id = household.id
       GROUP BY households.id
       HAVING COUNT(people.id) = 1
     ENDSQL
   end
end

puts Household.count_single_person_households
Household.single_person_households.each do |household|
   puts household.name
end

-Rob

NOTE: If you create a view called single_person_household.html.erb,
then you will need a route that maps to that view in your routes.rb
file, such as

map.connect 'single_person_household', :controller =>
'households', :action => 'single_person_household'

In your view, you will use @households_with_one.

By the way, before I post code, I test it, so I know that I am
actually answering the question which I understood to have been asked.

Sandy

Rob Biedenharn
Rob@AgileConsultingLLC.com http://AgileConsultingLLC.com/
rab@GaslightSoftware.com http://GaslightSoftware.com/

Rob,

I agree that SQL is more efficient than the Ruby code. However, not
every application requires the ultimate in database 'efficiency', and
my experience is that such efficiency, at the expense of coding time
and clarity, is often overrated

As far as the "original question" goes, he said, "I am assuming that
the output from this could be used to get the name field from
households." The fact that he wants the name field from the
households implies that he needs the array such that he can then
display the name field. The count, alone, doesn't supply the name
field.

Sandy

Worked perfectly again Sandy. Thanks

Bob