I store sql like select * from classes where owner_id = #{#current_user.id} in my database.
Can any one tell me how write out the above sql as ruby code so the #{#current_user.id} can be replaced dynamically as the id of the current signed in user?
Without any security/input validation in mind, you could do like this:
q_template = "select * from classes where owner_id = %s"
and to "populate" it, you could do like this:
sample_input = "40"
new_query = q_template % sample_input
# this will create q_template = "select * from classes where owner_id = 40"
If you end up using this, make sure you check your input for malicious things before feeding it to the database.
Related
Currently the only way I know to sub select in rails is with arel,
for exmaple -
sub = x.where(y:'x').project(:id)
select = a.where(a[:x_id].in(sub))
question is,
if x is using the acts as taggable on gem and need to filtered by a specific tag, use with tagged_with method.
How can I still achive same database efficiency, it looks like the tagged with method override the projection.
thanks,
You don't need Arel to build sub selects in Rails:
sub = X.where(y: 'x')
select = A.where(x_id: sub)
generates the following SQL, assuming A's table name is as and X's is xs:
SELECT "as".* FROM "as" WHERE "as"."x_id" IN (SELECT "xs"."id" FROM "xs" WHERE "xs"."y" = 'x')
Testing with tagged_with worked: A.where(x_id: X.tagged_with('my_tag')) generates the expected SQL, at least for Rails 5.1, version on which I've tested.
Edit
You can specify the column used inside the subselect if needed. If you don't specify it, the primary key column is the default:
sub = X.where(y: 'x').select(:x_y_id)
select = A.where(x_id: sub)
will generate the following SQL:
SELECT "as".* FROM "as" WHERE "as"."x_id" IN (SELECT "xs"."x_y_id" FROM "xs" WHERE "xs"."y" = 'x')
Ok, I feel really stupid for asking this, but it's driving me nuts and I can't figure it out. The docs say I should be able to use select AS in a Rails/ActiveRecord query. So:
d = Dvd.where(id: 1).select("title AS my_title")
Is a valid query and if I do a to_sql on it, it produces the expected SQL:
SELECT title AS my_title FROM `dvd` WHERE `dvd`.`id` = 1
However, d.my_title will give an error:
NoMethodError: undefined method `my_title' for #<ActiveRecord::Relation
I need to be able to use AS since the columns I want to retrieve from different joins have the same name so I can't access them the "regular" way and have to resort to using AS.
I also don't want to resort to using find_by_sql for future compatibility and a possible switch form Mysql to PostGresql.
Just to clarify, what I'm really trying to do is write this SQL in a Railsy way:
SELECT tracks.name AS track_name, artists.name AS artist_name, composers.name AS composer_name, duration
FROM `tracks_cds`
INNER JOIN `tracks` ON `tracks`.`id` = `tracks_cds`.`track_id`
INNER JOIN `artists` ON `artists`.`id` = `tracks_cds`.`artist_id`
INNER JOIN `composers` ON `composers`.`id` = `tracks_cds`.`composer_id`
WHERE cd_id = cd.id
The top example was just a simplification of the fact that SELECT AS will not give you an easy way to refer to custom fields which I find hard to believe.
ActiveRecord automatically creates getter and setter methods for attributes based on the column names in the database, so there will be none defined for my_title.
Regarding the same common names, why not just do this:
d = Dvd.where(id: 1).select("dvds.title")
You can write your sql query and then just pass into ActiveRecord's execute method
query = "SELECT title AS my_title FROM `dvd` WHERE `dvd`.`id` = 1"
result = ActiveRecord::Base.connection.execute(query)
So I have 2 tables actor and actor2role. The latter is a lookup (junction) table to relate actor, role and dvd. I need to create a query with aliases, so I have this method:
def self.remove_duplicate_by_id(id)
offendingActor = self.find(id).actor # get the actor's name
ids = self.find_by_sql("SELECT MIN(id) AS minId, MAX(id) AS maxId, actor FROM `dvd_actor` WHERE actor = '#{offendingActor}'")
rolesForOffender = ids.actor2role # throws error
end
The problem is that ids is not an ActiveRecord object so I can't use the actor2role method (which is a relationship I've established between the 2 tables in Rails and works when you do something like Actor.first.actor2role.
so the questions is: Am I doomed to do this manually and then issue another sql query to recreate what the actor2role method would accomplish or is there some way to do this with Rails objects?
I'd really like to do it all natively if possible because I also have to issue these queries:
UPDATE dvd_actor2role SET actorid = $d->minId WHERE actorId = $d->maxId");
DELETE FROM dvd_actor2role WHERE actorId = $d->maxId LIMIT 1");
Is this even possible?
In the end I went with this which seems to do the trick. If anyone can spot any code that could be optimized, or something inherently wrong (and feels like chiming in) please feel free to comment.
actorObject = self.find_by_id(id) # get the object because we need it below for other queries
offendingActor = actorObject.actor
ids = self.select("MIN(id) AS minId, MAX(id) AS maxId, id, actor").find_by_actor(offendingActor)
rolesForOffender = actorObject.actor2role
rolesForOffender.each do |r|
# first find out if the relationship already exists or we get a SQL error for the foreign key relationship.
exists = Actor2role.where("actorId = ? AND roleId = ?", ids.minId, r.roleId)
if exists.nil?
Actor2role.update_all("actorId = #{ids.minId}, actorId = #{ids.maxId}")
end
end
self.destroy(ids.maxId) # delete this guy in actor table
end
Using Ruby DBI, how can I check if a record set is empty? (without iterating through it or doing a count query)
sth = dbh.prepare("select * from things WHERE created_at > '#{start}'")
sth.execute
You could always just ask the result object:
res = sth.execute
res.num_rows
The operation will have to pull down all matching records, though, so if you only need a count, you might want to select that directly.
Also escape your SQL. You cannot just put arbitrary strings in there. This is better:
sth = dbh.prepare("select * from things WHERE created_at > '%s'" % sth.escape_string(start))
I've been able to make this code work using CodeIgniter's db->query as follows:
$sql =
'SELECT mapping_code,zone_name,installation_name
FROM installations,appearances,zones
WHERE
installations.installation_id = zones.installation_fk_id
AND appearances.installation_fk_id = installations.installation_id
AND appearances.zone_fk_id = zones.zone_id
AND
appearances.barcode = ?
';
return $this->db->query($sql,array($barcode));
The 'appearances' table throws a 'not unique table' error if I try
this using the Active Record class.
I need to join appearances on
both the zone and installations tables.
How can I do this?
Take a look at $this->db->join() if you wish to use the ActiveRecord class. Right now you are just using plain SQL which has nothing to do with the ActiveRecord stuff at all.
Does this work when run through a MySQL client like phpMyAdmin, Navicat, etc?