Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rails using sql variables in find_by_sql

I have this query:

SET @current_group = NULL; 
SET @current_count = 0; 
SELECT user_id, MIN( created_at ) as created_at, CASE WHEN @current_group = user_id THEN @current_count WHEN @current_group := user_id THEN @current_count := @current_count + 1 END AS c 
FROM notifies 
G    ROUP BY user_id, c 
ORDER BY id desc LIMIT 0 , 10

If i launch it it works

but if i put it in a find_by_sql method like:

Notify.find_by_sql("SET @current_group = NULL; SET @current_count = 0; SELECT user_id, MIN( created_at ) as created_at, CASE WHEN @current_group = user_id THEN @current_count WHEN @current_group := user_id THEN @current_count := @current_count + 1 END AS c FROM notifies GROUP BY user_id, c ORDER BY id desc LIMIT 0 , 10")

It returns this error:

Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SET @current_count = 0; SELECT user_id, MIN( created_at ) as created_at, CASE WH' at line 1:

How can i do?

thanks

like image 960
Luca Romagnoli Avatar asked May 31 '26 13:05

Luca Romagnoli


1 Answers

It's because find_by_sql only works with a single statement.

Setting variables happen in separate statements. set specifically is in the Database Administration section, number 12.4.4, and select is in Data Manipulation section, number 12.2.7. Consoles (usually) allow multiple statements, and keep the variables around, but ActiveRecord queries do not.

To allow multiple statements, I think you have to maintain a persistent connection with the database, which Rails doesn't do (edit: normally). But I'm not certain about that - if anyone else knows, I'd love a more definite reason.

Edit: actually, I have a solution for you. Try this:

items = YourModel.transaction do
  YourModel.connection.execute("SET @current_group = NULL;")
  YourModel.connection.execute("SET @current_count = 0;")
  # this is returned, because it's the last line in the block
  YourModel.find_by_sql(%Q|
    SELECT user_id, MIN( created_at ) as created_at, CASE WHEN @current_group = user_id THEN @current_count WHEN @current_group := user_id THEN @current_count := @current_count + 1 END AS c 
    FROM notifies 
    GROUP BY user_id, c 
    ORDER BY id desc LIMIT 0 , 10
  |)
end

All those run in a single transaction, and any variables / settings inside the transaction block will persist between queries. You're still bound to a single statement per query though. There might be a way to do it without an actual transaction wrapping the whole set, but I haven't looked for it - most likely you want one, or you now have a very specific thing to look for if you know you don't.

Enjoy!

like image 77
Groxx Avatar answered Jun 03 '26 04:06

Groxx



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!