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/