Search for database objects created on a specific day? - sql

I want to query my db for objects of an arbitrary model created on a certain day, what's a good way to do this?
In an sql query I used the following to query the objects created yesterday.
where (created_at AT TIME ZONE 'EDT') > (now() AT TIME ZONE 'EDT')::DATE - 1
and (created_at AT TIME ZONE 'EDT') < (now() AT TIME ZONE 'EDT')::DATE
I want to do the same thing in ruby with ActiveRecord calls.
Thanks!

An example, as a scope, for all items that were created today:
scope :created_today, -> { where('created_at between ? AND ?', Time.zone.now.beginning_of_day, Time.zone.now.end_of_day) }
More information at the PostgreSQL site

I would do the cast on the database and select the current date there, instead of doing it with a range. You can do this with DATE(created_at) (Will result in "2013-11-01") Then inject the param in the where:
Model.where("DATE(created_at) = ?", Date.today)
If it's used on more places then one it would be good to create a scope as Morner suggested:
scope :created_today { where("DATE(created_at) = ?", Date.today) }
with name:
scope :created_today_with_name lambda do |name|
where("DATE(created_at) = ? AND name = ?", Date.today, name)
end
or chain it:
scope :created_today_with_name lambda do |name|
where("DATE(created_at) = ?", Date.today).where(name: name)
end
Then call it as:
Model.created_today_with_name('Joe')

Related

Rails where condition with timestamp

I have query, that would return me data depending on its created_at timestamp
my query looks like
condition[:created_at] = " > #{Time.now - 2.days}"
model.where(condition)
and this return me following sql
...WHERE `model`.`created_at` = ' > 2000-01-01T02:00:00+02:00'
so here timestamp looks different from what in db
So how do i pass correct timestamp to match AR format?
ActiveSupport's #ago will help:
model.where("created_at > ?", 2.days.ago)
Also, I wrote a gem to contain common scopes for created_at queries and others: https://github.com/neighborland/scopy

Difference between postgresql time and rails 3 app time

Scenario: I want to get the nearest time in relation to my system time.
I have written the following method inside my model:
def self.nearest_class(day, teacher_id, hour_min)
day_of_week = '%' + day + '%'
if hour_min == "06:00:00"
where('frequency like ? AND teacher_id = ? AND (start_time >= ?)', day_of_week, teacher_id, hour_min).order('start_time ASC')
else
where('frequency like ? AND teacher_id = ? AND (start_time >= localtime(0))', day_of_week, teacher_id).order('start_time ASC')
end
end
When I tested the above method in rails console using Lecture.nearest_class('Monday', 1, 'localtime(0)'), the following query was returned together with some data:
SELECT "lectures".* FROM "lectures" WHERE (frequency like '%Monday%' AND teacher_id = 1 AND (start_time >= localtime(0))) ORDER BY start_time ASC
But I am expecting no record because my system time is greater than any start_time recorded in the database. I have used the query from the console to pgadmin3 to test if the results are same. However, pgadmin3 showed no results, as expected.
Are there differences in postgresql time and rails app time? How can I be able to check these differences? I tried Time.now in the console and it is the same as SELECT LOCALTIME(0) from pgadmin3. What should be the proper way to get the records for nearest system time?
To further set the context, here's my controller method:
def showlectures
#teacher = Teacher.login(params[:id]).first
#lecture_by_course = nil
if #teacher
WEEKDAY.each do |day|
hour_min = "06:00:00"
if day == Time.now.strftime("%A") # check if day of week is same e.g. 'Monday'
hour_min = "localtime(0)" # system time with format: HH:MM:SS
end
#lecture_by_course = Lecture.nearest_class(day, #teacher.id, hour_min).first
if #lecture_by_course
break
end
end
end
render json: #lecture_by_course, include: { course: { only: [:name, :id] }}
end
I solved this by just passing the server Time.now instead of having a SQL check. { :| d'oh }

Rails / Postgres: “must appear in the GROUP BY clause or be used in an aggregate function”

I'm using this method:
def self.lines_price_report(n)
Income.group('date(filled_at)').having("date(filled_at) > ?", Date.today - n).sum(:lines_price)
end
I'm getting this error in Heroku:
PG::Error: ERROR: column "incomes.filled_at" must appear in the GROUP BY clause
or be used in an aggregate function
How can I fix this? Thank you.
Executed query:
SELECT SUM("incomes"."lines_price") AS sum_lines_price, date(filled_at)
AS date_filled_at FROM "incomes"
HAVING (date(filled_at) > '2012-12-04')
GROUP BY date(filled_at) ORDER BY filled_at ASC
Expected result
[["2012-12-04", SUM_FOR_DATE], ["2012-12-05", SUM_FOR_DATE], ...]
Your mistake was to use filled_at in order by probably in default scope.
You can fix it using unscoped to eliminate default scopes:
Income.unscoped
.group('date(filled_at)')
.having("date(filled_at) > ?", Date.today - n)
.sum(:lines_price)
or
Income.unscoped
.group('date(filled_at)')
.having("date(filled_at) > ?", Date.today - n)
.sum(:lines_price)
.order('date(filled_at) ASC')
but I think that better will be to use where instead of having
Income.unscoped
.where("date(filled_at) > TIMESTAMP ?", Date.today - n)
.group('date(filled_at)')
.sum(:lines_price)
.order('date(filled_at) ASC')
SQLFiddle
You have to be careful about using TIMESTAMP because 2012-12-04 will become 2012-12-04 00:00:00 so if you don't want this day in result use Date.today - (n - 1)
If you create index on filled_at column
create index incomes_filled_at on incomes(filled_at);
migration:
add_index :incomes, :filled_at
and you have a lot of data in this table index will be used in filtering. So query should be much faster.
So just write both and test which is faster (you have to create index on filled_at if you don't have one).
I guess this is because you use date(filled_at) in GROUP BY but just filled at in ORDER. As I guess order is taken from default scope you need to overwrite it by reorder. I would suggest:
Income.sum(:lines_price).
group('date(filled_at)').
having("date(filled_at) > ?", Date.today - n).
reorder("date(filled_at) ASC")
When you want to use Group By on PostgreSQL, The select option should be required on the group by.
Income.select('filled_at').group('date(filled_at)').having("date(filled_at) > ?", Date.today - n).sum(:lines_price)

Rails3 records this year

In Rails3 how do I retrieve all records that have a date of this year?
I'd like to retrieve all my "Projects" where the "date" is this year.
I would suspect it would be something similar to:
#projects_this_year = Project.where(:date.Time.year == Time.now.year)
or
#projects_this_year = Project.where(:date >= ?, Time.now.year)
I'm reading through the docs, but haven't quite figured this out yet.
You could do something like this (Assuming a MySQL DB):
#projects_this_year = Project.where("year(date) = ?", Time.now.year)
or better:
#projects_this_year = Project.where("year(date) = year(utc_timetamp())")
which doesn't require loading the time library.
For reference: http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html
Edit
If you want it to be database independent then you can do something like this:
#projects_this_year = Project.where("date >= ?", Time.now.at_beginning_of_year)
You could also make this dynamic in the model if you needed specific months or years often or through params by expanding on #David's answer:
def self.get_year(date)
# sanitize if params
safe = ['2011', '2012', '2013']
if safe.include?(date)
where("year(created_at) = ?", Time.parse("#{date}-01-01 11:11:11 -0500").year)
else
scoped
end
end
EX:
Product.get_year('2012')
NOTE to get this to work on heroku please change the following:
where("date_part('year', created_at) = ?", Time.parse("#{date}-01-01 11:11:11 -0500").year
http://www.java2s.com/Code/PostgreSQL/Date-Timezone/datepartyeardate.htm

Is there a way to update an entire array or named_scope without using a loop in ruby on rails?

I create the following array using searchlogic named_scopes:
todos = Todo.asset_is("Email").asset_id_is(self.id)
For each value in the array, there is an attribute called original_date and current_date.
I need to make changes to those with some logic, such as:
difference = (original_date - date_entered) - self.days
original_date = date_entered + self.days
current_date = current_date - different
What I do not want to do is do an each do-loop. But I don't know if there's an alternative -- something like the "update" in SQL (but without needing to use SQL -- like using searchlogic)
Todo.update_all(["original_date = date_entered + %d, current_date = ... + %d",
self.days, self.days], ["id in (?)", todos.map(&:id)])