简体   繁体   中英

Combine Ruby variable in SQL statement rails to find number of days difference between two dates

This is my SQL query:

Battle.all.order("votes_count / #{(DateTime.now.to_i - created_at.to_i) / 86400}")

votes_count and created_at are battle columns; I want to divide votes_count by the day difference of DateTime now and battle created_at . Right now I get a syntax error; how can I make it work? I'm using PostgreSQL in my Rails 4 app.

EDIT

Changed ' to ";

The error I get:

NameError: undefined local variable or method 'created_at' for main:Object

created_at is battle column not ruby variable..

Thanks.

The first error is that your string interpolation isn't getting applied because you have used a single quoted string. You need to use double quoted strings to make string interpolation work correctly.

Try this, instead:

Battle.all.order("votes_count / #{(DateTime.now.to_i - created_at.to_i) / 86400}")

This will allow interpolation to work correctly, but will cause you to encounter the second error, which occurs while processing the interpolation.

The second error is caused by attempting to use the value of created_at in the interpolation, which occurs before the SQL is run. Instead, refactor the query to use this form:

Battle.all.order("votes_count / ('#{DateTime.now}' - created_at) / 86400")

In this code, only the DateTime.now value is included in the interpolation, the parentheses have been moved outside the interpolation value, and the result of #{DateTime.now} is surrounded with single quotes to make the resulting value SQL-friendly. to_i has been removed from both expressions to allow the date subtraction to be done in the SQL.

Note that caution should be used with SQL datetime subtraction, as the syntax of datetime subtraction differs widely from one database product to another, as does the result of the subtraction operation.

Examples of subtracting datetime values in some common databases:

| Database  | Syntax                                                | Result Units |
|-----------|-------------------------------------------------------|--------------|
| Postgres  | datetime1 - datetime2                                 | Interval     |
| MySQL     | TIMEDIFF(datetime1, datetime1)                        | Seconds      |
| SQLServer | DATEDIFF(second, datetime1, datetime2)                | Seconds      |
| DB2       | SECOND(datetime1,s) - SECOND(datetime2,s)             | Seconds      |
| SQLite    | strftime('%s', datetime1) - strftime('%s', datetime2) | Seconds      |

This should solve your problem :

to_date_str = "to_date('#{DateTime.now.strftime '%d/%m/%Y %H:%M'}', 'DD/MM/YYYY HH24:MI')"

str = "votes_count / (EXTRACT(epoch FROM (#{to_date_str} - created_at))/86400)::int" 

Battle.order(str)

The first problem you had was a syntax error.

Your syntax error is due to the fact that you are using single quotes ' instead of double quotes " .

With the single quote the expression #{(DateTime.now.to_i - created_at.to_i) / 86400} will not be replaced by its value, whereas with double quotes it will be computed and inserted in the string.

So to solve your syntax error use :

Battle.all.order("votes_count / #{(DateTime.now.to_i - created_at.to_i) / 86400}")

But it did not completely solved your problem. You got NameError: undefined local variable or method 'created_at' for main:Object . Why ?

You got this error because you are computing the string "votes_count / #{(DateTime.now.to_i - created_at.to_i) / 86400}" before the order gets executed. So at this point ruby does not know which Battle is being processed. In fact it does not know since NO Battle is being processed at this point. That's why ruby tells you there is no "created_at". The solution is to use SQL instead of trying to do it in ruby.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM