Rails - update directly in SQL - mysql

Delving into the documentation and the api, I seem to be missing how to update one field in multiple rows at once.
Something like
Table.select(:field).update("update to this").where(id: 4,5,6)
would be nice.
Does something like this exist? It would be much better than having to store everything in an array, set it to a value, and calling save every time.

You can use the update_all method, for example:
Table.update_all("field = 'update to this'", ["id in (?)", ids])

Related

Same ID which occurs multiple times with SQL-IN Operator at rails

I use the following sql statement:
Keyword.where("id IN (#{params[:keyword_ids]})").order("find_in_set(id, '#{params[:keyword_ids]}')")
The Problem at this statement is that if "keyword id" hold the same id more than ones, the call returns it only ones.
But I need the same number(not fulfilled) as well as the same order(which is fulfilled with this statement) which occurs in the array, independent if the same id occurs more than ones.
How should I change that statement to fix.
Thanks, dot
Well, that's not a bug, it's a feature ;)
My first recommendation would be to sanitize your input. Passing params[:keyword_ids] directly to the database, despite the help that the Rails framework does, is prone to lead to some kind of vulnerability sooner or later.
Secondly, the easiest solution is probably to keep the query as is, convert the results to a map and map the input params to the result.
Sth like
keywords = Keyword.where('id IN (?)', checked_keyword_ids)
keyword_map = Hash[keywords.map { |kw| [kw.id, kw] }]
checked_keyword_ids.map { |id| keyword_map[id] }

Creating an OR statement using existing conditions hash

I am working on a problem where I need to add an OR clause to a set of existing conditions. The current conditions are built in a hash in a method and at the end, they are used in the where clause. Here is a simplified example:
...
conds.merge!({:users => {:archived => false}})
Model.where(conds)
I am trying to add an OR clause to the current set of conditions so it would be something like '(conditions) OR new_condition'. I'd like to add the OR statement without converting each addition to the conds hash into a string. That would be my last option. I was hoping someone has done something like this before (without using Arel). I seem to recall in Rails 2 there was a way to parse a conditions hash using a method from the model (something like Model.some_method(conds) would produce the where clause string. Maybe that would be a good option to just add the OR clause on to that string. Any ideas are appreciated. Thank you for your help!
I found a way to do what I needed. Instead of changing all of the conditions that I am building, I am parsing the conditions to SQL using sanitize_sql_for_conditions. This is a private method in ActiveRecord, so I had to put a method on the model to allow me to access it. Here is my model method:
def self.convert_conditions_hash_to_sql(conditions)
self.sanitize_sql_for_conditions(conditions)
end
So, once I convert my conditions to text, I can add my OR clause (along with the appropriate parentheses) to the end of the original conditions. So, it would go something like this:
Model.where('(?) OR (model.type = ? AND model.id IN(?))', Model.convert_conditions_hash_to_sql(conds), model_type, model_id_array)

DBIx::Class update only one row

I am using DBIx::Class and I would like to only update one row in my table. Currently this is how I do it:
my $session = my_app->model("DB::Session")->find(1);
$session->update({done_yn=>'y',end_time=>\'NOW()'});
It works, but the problem is that when it does find to find the row, it does this whole query:
SELECT me.id, me.project_id, me.user_id, me.start_time, me.end_time, me.notes, me.done_yn FROM sessions me WHERE ( me.id = ? ): '8'
Which seems a bit much when all I want to do is update a row. Is there anyway to update a row without having to pull the whole row out of the database first? Something like this is what I am looking for:
my_app->model("DB::Session")->update({done_yn=>'y',end_time=>\'NOW()'},{id=>$id});
Where $id is the WHERE id=? part of the query. Does anyone know how to do this? Thanks!
You can run update on a restricted resultset which only matches this single row:
my_app->model("DB::Session")->search_rs({ id=> 1 })->update({done_yn=>'y',end_time=>\'NOW()'});
I suggest you use a DateTime->now object instead of literal SQL for updating the end_time column because it uses the apps servers date and time instead of the database servers and makes your schema more compatible with different RDBMSes.
Do you have a check if the row was found to prevent an error in case it wasn't?
You might want to use update_or_create instead.
You could use the "columns" attribute:
my $session = my_app->model("DB::Session")->find(1, {columns => "id"});

What is the "Rails Way" of doing a query with an OR clause using ActiveRecord?

I'm using Rails 3 with a MySQL database, and I need to programmatically create a query like this:
select * from table where category_name like '%category_name_1%'
OR category_name like '%category_name_2%'
(...snip...)
OR category_name like '%category_name_n%'
Given the table size and the project scope (500 rows at most, I think), I feel that using something like thinking sphinx would be overkill.
I know I could simply do this by writing the query string directly, but wanted to know if there's an ActiveRecord way to do this. There's no mention of this on the official guide, and I've been googling for a long while now, just to end empty-handed :(
Also, is there a reason (maybe a Rails reason?) to not to include the OR clause?
Thanks!
Assuming you have an array names with category names:
Model.where( names.map{"category_name LIKE ?"}.join(" OR "),
*names.map{|n| "%#{n}%" } )
you should google first, there is already an answer.
Look here and then here
and you'll get something like this:
accounts = Account.arel_table
Account.where(accounts[:name].matches("%#{user_name}%").or(accounts[:name].matches("%#{user_name2}%")))
If you look at the guide, they have examples that can easily be modified to this:
Client.where("orders_count = ? OR locked = ?", params[:orders], false)
Mysql has a regexp function now that can clean things up a bit, assuming there's no regex metachars in your category names:
Table.where "category_name regexp '#{names.join('|')}'"

SHA salt in MySQL query

I am setting a cookie. Something like:
$_COOKIE['test'] = SHA1('124'.'mysalt');
Now 124 is my id which I want. So in my MySQL table, I am trying to run a query like:
$sql = ("SELECT * FROM users WHERE SHA1(`id`) = '".mysql_real_escape_string($_COOKIE['test'])."'");
How to add the "mysalt" to the SQL query? Because else I want get the correct id.
Use can use Concat() for that.
SELECT ... Sha1( Concat(`id`, 'mysalt') )=...
The query should be:
$sql = ("SELECT * FROM users WHERE SHA1(CONCAT(`id`,`mysalt`)) = '".mysql_real_escape_string($_COOKIE['test'])."'");
if I understand correctly what you're trying to do.
The solutions already provided probably will work just fine, however are you certain you want to do this? If the field "id" is really a distinct identification you can use "LIMIT 1" to stop mysql from searching thru all your items. Another thing is, why don't you use a separate precomputed field for this? I mean in every query mysql unnecessarily needs to compute all these sha1 values.. One last thing. I'm uncertain why you are using your approach, but my best guess is to implement some sort of session key. I thing this is a bad idea for a couple of reasons: If someone gets holds on your salt, he has access to all your accounts. If someone sniffs one "session" he can reuse it whenever he wants to. Choosing a weak salt could have serious consequences. HTH.
Use CONCAT:
$sql = ("SELECT * FROM users WHERE SHA1(CONCAT(`id`,'mysalt')) = '".mysql_real_escape_string($_COOKIE[''test''])."'");