I ran into a little problem I have a has_many through relationship here is the code for the models
class User < ActiveRecord::Base
has_many :friendships
has_many :followings, :through => :friendships, :foreign_key => "followed_id"
end
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :following, :class_name => "User", :foreign_key => "followed_id"
end
now at the console I can type u = User.first and then u.friendships.first.following this gives me the first user u is following, but when I type u.friendships.last.following I get this error
the SELECT statement from u.friendships.first.following
Friendship Load (0.3ms) SELECT `friendships`.* FROM `friendships` WHERE `friendships`.`user_id` = 208 LIMIT 1
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 209 LIMIT 1
and the SELECT statement from u.friendships.last.following
Friendship Load (0.3ms) SELECT `friendships`.* FROM `friendships` WHERE `friendships`.`user_id` = 208 ORDER BY `friendships`.`` DESC LIMIT 1
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'friendships.' in 'order
clause': SELECT `friendships`.* FROM `friendships` WHERE `friendships`.`user_id` = 208
ORDER BY `friendships`.`` DESC LIMIT 1
if I then run u.friendships and then u.friendships.last.following again, I don't get the error anymore, why is that?
Heres my sql output for friendships, straight from your code on Rails 3.2.9 / postgresql:
# u.friendships.first.following
Friendship Load (0.9ms) SELECT "friendships".* FROM "friendships" WHERE "friendships"."user_id" = 1 LIMIT 1
# u.friendships.first.following
Friendship Load (1.3ms) SELECT "friendships".* FROM "friendships" WHERE "friendships"."user_id" = 1 ORDER BY "friendships"."id" DESC LIMIT 1
So for some reason for me, id is getting picked up automatically in ORDER BY "friendships"."id" and it works. Maybe your problem has something to do with your DB?
#Statements used to create the db for reproducing this problem
CREATE TABLE users (id SERIAL PRIMARY KEY)
CREATE TABLE friendships (
id SERIAL PRIMARY KEY,
user_id integer
followed_id integer
);
Related
I created following model
module UserInfo
class User < ActiveRecord::Base
self.table_name = 'vUserDetails'
default_scope { order(date_entered: :desc) }
end
end
How can I produce the following query using this model?
SELECT TOP 1 Column1
FROM vUserDetails
WHERE vUserDetails.UserID = #user_id
ORDER BY DateEntered DESC
UserInfo.first will give you the first record, ordered by the order defined in your default_scope.
If you really want to select only the column Column1, then you would use UserInfo.select(:Column1).first.
I have a Sequel-based class that I need to do some summaries on.
I'm doing a group_and_count, and I can see that it's generating the right query. However, when I try to access the results, Sequel is trying to coerce the rows into the class I'm accessing through:
[33] pry(main)> grouped = Pancakes::Stack.active.group_and_count('health_state')
=> #<Sequel::Mysql2::Dataset: "SELECT 'health_state', count(*) AS `count` FROM `pancakes_stacks` WHERE (`deleted_at` IS NULL) GROUP BY 'health_state'">
[34] pry(main)> grouped.each_entry { |row| puts row }
I sequel: (0.001344s) SELECT 'health_state', count(*) AS `count` FROM `pancakes_stacks` WHERE (`deleted_at` IS NULL) GROUP BY 'health_state'
#<Pancakes::Stack:0x000000089251a0>
=> #<Sequel::Mysql2::Dataset: "SELECT 'health_state', count(*) AS `count` FROM `pancakes_stacks` WHERE (`deleted_at` IS NULL) GROUP BY 'health_state'">
[35] pry(main)> grouped.first
I sequel: (0.001502s) SELECT 'health_state', count(*) AS `count` FROM `pancakes_stacks` WHERE (`deleted_at` IS NULL) GROUP BY 'health_state' LIMIT 1
I sequel: (0.001243s) SELECT * FROM `pancakes_stacks` WHERE (`id` IS NULL) LIMIT 1
=> #<Pancakes::Stack:0x44b068c>
I can get what I need by working around the ORM stuff, but that appears to require me to re-implement the active method above, and figure out how to get the table name from the class name:
[38] pry(main)> groupie = grouped.db[:pancakes_stacks].where(deleted_at:nil).group_and_count(:health_state)
=> #<Sequel::Mysql2::Dataset: "SELECT `health_state`, count(*) AS `count` FROM `pancakes_stacks` WHERE (`deleted_at` IS NULL) GROUP BY `health_state`">
[39] pry(main)> groupie.each_entry { |row| puts row }
I sequel: (0.001598s) SELECT `health_state`, count(*) AS `count` FROM `pancakes_stacks` WHERE (`deleted_at` IS NULL) GROUP BY `health_state`
{:health_state=>nil, :count=>3}
{:health_state=>"healthy", :count=>10}
Isn't there an easier way? I've spent a lot of time on the querying page, but none of the examples show how to access the results.
You probably just want to add a .naked to your dataset, which will make the dataset return hashes instead of model objects.
I personally use the square brackets method to get the :count attribute for a row:
row[:count]
I have a data model where a Customer has many Orders. I now need to extract all of the customers that have only placed 1 order and I am scratching my head trying to figure out what to do.
Use this
Customer.joins(:orders).group(:id).having("count(orders.id) = 1")
this will create a query like:
SELECT "customers".* FROM "customers" INNER JOIN "orders" ON
"orders"."customer_id" = "customers"."id" GROUP BY id HAVING
count(orders.id) = 1
You would get all of the customers that have placed exactly 1 order.
To avoid ambiguous reference to the ID field, this would be used:
Customer.joins(:orders).group("customers.id").having("count(orders.id) = 1")
which would generate the following SQL:
SELECT "customers".* FROM "customers" INNER JOIN "orders" ON "orders"."customer_id" =
"customers"."id" GROUP BY customers.id HAVING count(orders.id) = 1
Consider Customer and Order are ActiveRecord classes and consider you have a code line
belongs_to :customer in your Order class definition. And also consider that Order table have a foreign_key or index column named customer_id do as below to get this Customer objects.
Customer.joins(:orders).where("count(orders.id) = 1")
I'm trying to destroy an object using a call similar to
MyObject.destroy_all({:user_id => current_user.id, :item_type_id => params[:type_id], :item_id => params[:item_id]})
Rails generates this as a SQL commands:
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
MyObject Load (0.2ms) SELECT `my_objects`.* FROM `my_objects` WHERE `my_objects`.`user_id` = 1 AND `my_objects`.`item_type_id` = 3 AND `my_objects`.`item_id` = 9
(0.1ms) BEGIN
SQL (0.4ms) DELETE FROM `my_objects` WHERE `my_objects`.`` = NULL
The last SQL Statement causes this error (which makes sense)
Mysql2::Error: Unknown column 'my_objects.' in 'where clause': DELETE FROM `my_objects` WHERE `my_objects`.`` = NULL
Am I doing something wrong?
Rails 3.2.1
mysql2 0.3.11
mysql 5 (I think)
I have a relationship table :
create_table "animal_friends", :force => true do |t|
t.integer "animal_id"
t.integer "animal_friend_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "status_id", :default => 1
end
linking animals to others. Best way to retreive associations in SQL is :
SELECT animals.*
from animals join animal_friends as af
on animals.id =
case when af.animal_id = #{id} then af.animal_friend_id else af.animal_id end
WHERE #{id} in (af.animal_id, af.animal_friend_id)
And I can't find a way to create a proper has_many relation in rails with this. Apparently, there's no way to provide joining conditions for has_many.
I'm currently using a finder_sql :
has_many :friends, :class_name => "Animal", :finder_sql => 'SELECT animals.* from animals join animal_friends as af on animals.id = case when af.animal_id = #{id} then af.animal_friend_id else af.animal_id end ' +
'WHERE #{id} in (af.animal_id, af.animal_friend_id) and status_id = #{Status::CONFIRMED.id}'
but this method has the great disadvantage of breaking activerecord magic. For instance :
#animal.friends.first
will execute the finder_sql without limit, fetching thousands of rows, then taking the first of the array (and loosing several precious seconds / req).
I guess it's a missing feature from AR, but I'd like to be sure first :)
Thanks
You could solve this on the database level with a view, which would be the correct method anyway.
CREATE VIEW with_inverse_animal_friends (
SELECT id,
animal_id,
animal_friend_id,
created_at,
updated_at,
status_id
FROM animal_friends
UNION
SELECT id,
animal_friend_id AS animal_id,
animal_id AS animal_friend_id,
created_at,
updated_at,
status_id
FROM animal_friends
)
If you dont want to have double entries for friends with relations both ways you could do this:
CREATE VIEW unique_animal_friends (
SELECT MIN(id), animal_id, animal_friend_id, MIN(created_at), MAX(updated_at), MIN(status_id)
FROM
(SELECT id,
animal_id,
animal_friend_id,
created_at,
updated_at,
status_id
FROM animal_friends
UNION
SELECT id,
animal_friend_id AS animal_id,
animal_id AS animal_friend_id,
created_at,
updated_at,
status_id
FROM animal_friends) AS all_animal_friends
GROUP BY animal_id, animal_friend_id
)
You would need a way to decide which status_id to use in case there are two conflicting ones. I chose MIN(status_id) but that is probably not what you want.
In Rails you can do this now:
class Animal < ActiveRecord::Base
has_many :unique_animal_friends
has_many :friends, :through => :unique_animal_friends
end
class UniqueAnimalFriend < ActiveRecord::Base
belongs_to :animal
belongs_to :friend, :class_name => "Animal"
end
This is out of my head and not tested. Also, you might need some plugin for view handling in rails (like "redhillonrails-core").
There is a plugin that does what you want.
There is a post about it here.
There is an alternative here.
Both allow you do to joining conditions and are just using lazy initialization.
So you can use dynamic conditions. I find the former prettier, but you can use the latter if you don't want to install plugins.
The two ways to create many to many relationships in active record are has_and_belongs_to_many and has_many :through. This site critiques the differences between the two. You don't have to write any SQL using these methods.