How can I write this rails query in proper SQL query ?
time = (Time.now - 1.days).beginning_of_day
Assignment.where("created_at >= :time OR updated_at >= :time", time: time).pluck(:assignable_id).uniq
Can you help me to convert this in SQL Query ?
select distinct assignable_id from assignments where created_at >= getDate() && updated_at >= getDate();
I need help in how to get date while querying?
You can get the date using MySQL using CURDATE().
Your query would look like this:
select distinct assignable_id from assignments where created_at >= CURDATE() AND updated_at >= CURDATE();
There is no way to use inequality comparison directly using just ActiveRecord, you will have to use Arel that is a lower level api that ActiveRecord is built on. Luckily you can mix and match them most of the time.
time = (Time.now - 1.days).beginning_of_day
assignments = Assignment.arel_table
Assignment.where(
assignments[:created_at].gteq(time).or(assignments[:updated_at].gteq(time))
).distinct.pluck(:assignable_id)
Passing the current date in from ruby code is a good solution most of the time. However if you do want to use database functions, then Arel is here to help again.
time = Arel::Nodes::NamedFunction.new('getDate', [])
# or
time = Arel::Nodes::NamedFunction.new('CURDATE', [])
# or any other function you can think of
Arel does seem more complicated than just writing raw sql at first glance, but the key is reusability and safety. Arel snippets are easy to reuse by just making them into methods. It's also easy to make injection mistakes using raw sql, Arel will prevent you from making those in most™️ cases.
Related
I have table with UNIX sql time stamp like 1615582447 in receivedOn.
I am not sure, how to work with this timestamp in case of some interval.
What is the correct way how to perform sql according to user timezone?
My current code is (for Europe/Prague timezone):
FROM_UNIXTIME(receivedOn) > ("2021-03-07T23:00:00.000Z") AND FROM_UNIXTIME(receivedOn) < ("2021-03-2021-03-08T22:59:59.999Z")
But this select return data outside interval.
What is the correct way or...better?
this can#t work in that form, as you have no correct mysql time to compare it with
SELECT FROM_UNIXTIME(1615582447 ) > CONVERT("2021-03-07T23:00:00.000Z", datETIME)
returns true or 1
you should always use mysql conform dates and time, to use in your query, and let the programming language convert it into the right form
I need to take data from DB, but only when ReturnDate column represents date in the future.
When i do SQL query from DB its working
SQL code:
SELECT * FROM `injuries_suspensions` WHERE `Type` = 'injury' and NOW() < `ReturnDate`
But when i use similar version in Laravel DB query builder, i dont get any data (when i erase where with NOW() i get all data so thats the problem)
Lravel code:
$injuries = DB::table('injuries_suspensions')->where('Type', '=', 'injury')->where(DB::raw('NOW()'), '<', 'ReturnDate')->get();
P.S
dates in DB are far in the future 2020-11-30.
You want to use the simplified version whereRaw instead:
$injuries = DB::table('injuries_suspensions')
->where('Type', '=', 'injury')
->whereRaw('ReturnDate > NOW()')->get(); //see I changed the comparison sides
Observation is that, you're not using whereRaw properly: all should be inside a single string see laravel's example.
You can also pass the timestamp/date directly when building the query, using Carbon see:
use Carbon/Carbon;
....
$injuries = DB::table('injuries_suspensions')
->where('Type', '=', 'injury')
->where('ReturnDate', '>', Carbon::now()->toDateTimeString())->get();
PS: Beware though of timezone difference of your db and your server.
Here is my logic
I want to get the closest 4 more expensive mobiles to a specific mobile #mobile but under one condition the difference between the release dates of the two mobiles is not more than a year and half
Here is the query
high = Mobile.where("price >= #{#mobile.price} AND id != #{#mobile.id} AND visible = true").where("ABS(release_date - #{#mobile.release_date}) > ?", 18.months).order(price: :ASC).first(4)
The first .where() works perfectly but the second is not working and I get 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 '00:00:00 UTC) > 46656000) ORDER BY `mobiles`.`price` ASC LIMIT 4' at line 1: SELECT `mobiles`.* FROM `mobiles` WHERE (price >= 123123.0 AND id != 11 AND visible = true) AND (ABS(release_date - 2016-04-10 00:00:00 UTC) > 46656000) ORDER BY `mobiles`.`price` ASC LIMIT 4
I think now you can get my logic. What is the right syntax to achieve it?
A couple of tips here:
It is a dangerous practice to concatenate variables into your queries using the "#{}" operator. Doing so bypasses query parameterization and could leave your app open to SQL injection. Instead, use "?" in your where clause.
The reason MySQL is giving you an error is because you are concatenating a string into your query without encapsulating it in quotes.
With these two things in mind, I would start by refactoring your query like so:
high = Mobile.where("price >= ?", #mobile.price)
.where.not(id: #mobile.id)
.where(visible: true)
.where("ABS(release_date - ?) > 46656000", #mobile.release_date)
.order(price: :ASC).first(4)
You will note that I replaced 18.months with 46656000. This saves a few clock cycles in the Rails app. Depending on your database schema, the last where clause may not work. The modification below may end up working better.
As a further refinement, you could refactor your last where clause to look for a release date that is between 18 months before #mobile.release_date and 18 months after. The saves your MySql database from having to do the math on each record and may lead to better performance:
.where(release_date: (#mobile.release_date - 18.months)..(#mobile.release_date + 18.months) )
I do not know your database schema, so you may run into date conversion problems with the code above. I recommend you play with it in the Rails console.
Use a Range to query between dates/times:
Mobile.where("price >= ?", #mobile.price)
.where.not(id: #mobile.id)
.where(release_date: 18.months.ago..Time.now)
.order(price: :ASC)
.first(4)
In the console I can query my Job table like this: jobs = Job.where("created_at <= ?", Time.now)
I want to get jobs that were created this year. If I get an individual job I can use job.created_at.year but if I try to use jobs = Job.where("created_at.year = ?", 2014) I get the following error:
SQLite3::SQLException: no such column: created_at.year
I think I understand that the problem is I'm trying to mix Ruby into a SQL query but I'm not sure how to get around it. Understanding this would help me create much more powerful ActiveRecord queries.
EDIT I found that I can do this using .map like so: Job.all.map { |j| j if j.created_at.year == 2014 }. Is that the best way to do this if I want to gather a collection of jobs that requires calling a method on an attribute?
Try this:
Job.where(:created_at => Time.now.beginning_of_year..Time.now.end_of_year)
Since, calling Time.now could potentially give two different years (before and after midnight on December 31) it would be better to first create a variable for the current time before it is used in a query:
current_time = Time.now
Job.where(:created_at => current_time.beginning_of_year..current_time.end_of_year)
I am using MySQL database and I have a datetime field inside it. In my rails application I have to fire a Query similar to the following,
MyTable.all(:conditions=>{my_date.to_date=>"2010-07-14"})
The my_date field is of datatype datetime. I should omit the time and should directly compare it with a date (but I am encountering an error near my_date.to_date due to obvious reasons). How to write an ActiveRecord Query for this scenario?
Thanks.
MyTable.all(:conditions=>["DATE_FORMAT(my_date, '%Y-%d-%m')=?", "2010-07-14"])
EDITED i think it should be
MyTable.all(:conditions=>["DATE_FORMAT(my_date, '%Y-%m-%d')=?", "2010-07-14"])
Here is an efficient solution if you index the date column:
d = "2010-07-14".to_date
MyTable.all(:conditions=>["my_date BETWEEN ? AND ?",
d.beginning_of_day, d.end_of_day)