ActiveRecord DateTime comparison - ruby-on-rails-3

I understand that Rails store the Time in UTC.
I have out_date and in_date instances of DateTime
I have this query:
reservations = Reservation.where("bookable_id = :bookable_id AND bookable_type = :bookable_type AND status <> :status AND ((:out_date >= check_out_date AND :out_date <= check_in_date) OR (:in_date <= check_in_date AND :in_date >= check_out_date) OR (:out_date <= check_in_date AND :in_date >= check_in_date))", :bookable_id => params[:bookable_id], :bookable_type => params[:bookable_type], :status => Reservation::STATUS_CHECKED_IN, :out_date => out_date, :in_date => in_date)
I always get a null set even though I should get a return tuple.
I have tried these varients:
out_date.utc
out_date.utc.to_s(:db)
Nothing seems to be working. How to construct this query?
Controller Code:
in_date = DateTime.strptime params[:checkin], "%Y-%m-%d %H:%M"
out_date = DateTime.strptime params[:checkout], "%Y-%m-%d %H:%M"
#check collision with non-recurring reservations
reservations = Reservation.where("bookable_id = :bookable_id AND bookable_type = :bookable_type AND status <> :status AND ((:out_date >= check_out_date AND :out_date <= check_in_date) OR (:in_date <= check_in_date AND :in_date >= check_out_date) OR (:out_date <= check_in_date AND :in_date >= check_in_date))", :bookable_id => params[:bookable_id], :bookable_type => params[:bookable_type], :status => Reservation::STATUS_CHECKED_IN, :out_date => out_date, :in_date => in_date)
logger.info(reservations)
if !reservations.empty?
#error_message = "This Asset has already been reserved for those dates"
return
end
Also on the Rails Console, the simpler query fails:
1.9.3p0 :007 > Reservation.find_by_id 31
Reservation Load (0.7ms) SELECT `reservations`.* FROM `reservations` WHERE `reservations`.`id` = 31 LIMIT 1
=> #<Reservation id: 31, bookable_id: 11, bookable_type: "Asset", user_id: 1, check_out_date: "2012-07-07 08:00:00", check_in_date: "2012-07-07 10:00:00", notes: "rec", status: "Ready", is_recurring: true, repeat_count: 5>
1.9.3p0 :009 > Reservation.where " ? >= check_out_date AND ? <= check_in_date",DateTime.new(2012,7,7,9),DateTime.new(2012,7,7,9)
Reservation Load (0.5ms) SELECT `reservations`.* FROM `reservations` WHERE ( '2012-07-07 09:00:00' >= check_out_date AND '2012-07-07 09:00:00' <= check_in_date)
=> []

I read the updated code and, apparently, it's correct. Make sure the out_date and in_date are DateTime objects and not null objects.
You don't need to format the query, it should be handled for you.
If you want to debug the query SQL, you can use the .to_sql
reservations = Reservation.where("...") # your query
puts reservation.to_sql
# => "SELECT ..."
Print the query and check if the value is correctly formatted.

The mistake was because of the the database time being stored in UTC and the local time being different and not satisfying the query condition.

Related

Rails: Two 'where' queries - each works individually, but not together

I want to write something like:
#meeting_requests = Meeting.where('meeting_time >= ? AND requestee_id IS ?
AND status = ?', Date.today, nil, "Active")
.joins(:requestor)
.where('birthyear >= ? AND birthyear <= ?',
current_user.birthyear - 10,
current_user.birthyear + 10 )
This works:
#meeting_requests = Meeting.where('meeting_time >= ? AND requestee_id IS ?
AND status = ?', Date.today, nil, "Active")
And this works:
#meeting_requests = Meeting.joins(:requestor)
.where('birthyear >= ? AND birthyear <= ?',
current_user.birthyear - 10,
current_user.birthyear + 10 )
And something like this works:
Meeting.joins(:requestor).where('birthyear > ?', 1900).where(status: "Active")
but I need to do a greater than query on the meeting_time, so I need to write it as a string I think?
But together both sql queries produce an error of: ambiguous column name: status: SELECT
I feel like I'm so close... what am I missing here?
This is a message that appears when it is not clear which table the column comes from. This should work:
...rest_of_statement.where('meetings.status' => 'Active')

Rails3/ActiveRecord: select sum with parameters?

Give the following (already simplified) query in SQLite:
def self.calculate(year, month, user_id, partner_id)
where(':user_id = entries.user_id OR :partner_id = entries.user_id', {
:user_id => user_id,
:partner_id => partner_id
}).
where('entries.date <= :last_day', {
:last_day => Date.new(year, month, 1).at_end_of_month
}).
select('sum(case when joint = "f" and user_id = :user_id then amount_calc else 0 end) as sum_single' , {
:user_id => user_id
}).
group("strftime('%Y-%m', date)")
end
The full query has more sums with different case when statements and some of them depend on whether it is user_id oder partner_id. Unfortunately, Rails complains as select does not take the second parameter with the substitutions like where does. Is there any way to achieve what I want without running two queries, one for user_id and one for partner_id?
One can be so blind....instead of:
select('sum(case when joint = "f" and user_id = :user_id then amount_calc else 0 end) as sum_single' , {
:user_id => user_id
}).
just build the string:
select('sum(case when joint = "f" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_single').
As nobody answered, this is for the archives :)
Edit: Sorry, beware of that: as noted below, this is vulnerable.

Rails Sql Query for MONTH AND YEAR

I'm trying to show sales transactions for the current month and year in my index view.
This is what I've put in my sales controller:
def index
#sales = show_sales_for_current_month(#sales)
which uses this method in the SalesHelper
def show_sales_for_current_month(sales)
sales = Sale.find(:all,
:conditions => ["MONTH(date) = ? AND YEAR(date) =?",
Date.today.month, Date.today.year])
end
where date is a date data type.
but i'm getting the following controller error:
SQLite3::SQLException: no such function: MONTH: SELECT "sales".* FROM "sales" WHERE (MONTH(date) = 4 AND YEAR(date) =2011)
I've looked around at posts and it seems like that is the correct function, so what am I doing wrong? Thanks!
Hey there, I have a gem called by_star which could help you with these kinds of queries. In your case you would only need to do this in your controller:
#sales = Sale.by_month
by_star takes care of working out what month and year it is, as well as the messy SQL.
The functions MONTH and YEAR do not exist in SQLite3. You can take an approach like this (taken from my current project):
model entry.rb:
def self.all_entries_year(year, user_id)
where('entries.user_id = :id', :id => user_id ).
where(':first_day <= entries.date AND entries.date <= :last_day', {
:first_day => Date.new(year, 1, 1),
:last_day => Date.new(year, 12, 31)
}).
order('date desc')
end
EDIT:
Put this in your model: sales.rb (I assume, it has the field date)
def self.show_sales_for_current_month(year, month)
mydate = Date.new(year, month, 1)
where(':first_day <= sales.date AND sales.date <= :last_day', {
        :first_day => mydate,
        :last_day => mydate.at_end_of_month
  }).
order('date')
end
In MySQL this works, but you are using SQLite3 so you need to modify your query to use the SQLite3 version of MONTH and YEAR, which means using the strftime function:
sales = Sale.find(:all,
:conditions => ["strftime('%m', date) = '?' AND strftime('%Y', date) = '?'",
'%02d' % Date.today.month, Date.today.year])

How do I search by date in PGSQL?

I am getting this error:
ActionView::Template::Error (PGError: ERROR: operator does not exist: timestamp without time zone ~~ unknown LINE 1: ... "articles" WHERE ("articles"."created_at" LIKE '2010...
I have an archive controller where I can dynamically display articles by genre and year, month and day, with whichever of those fields are available in the url. In mysqlite, I had this index action:
def index
filter_title
#articles = Article.where(:created_at.matches % date_builder, :genre.matches % genre_builder).order("created_at DESC")
respond_to do |format|
format.json { render :json => #articles }
format.xml { render :xml => #articles }
format.html
end
end
And this date_builder function
def date_builder
#date = ""
#date += params[:year] if !(params[:year].nil?)
#date += "-" + params[:month] if !(params[:month].nil?)
#date += "-" + params[:day] if !(params[:day].nil?)
#date += "%"
#date
end
that would use metawhere to find dates that matched the part of the string that I supplied. This worked perfectly in Sqlite. I have migrated my application onto heroku and PGSQL, and it doesn't allow me to do this. What is the solution? Thank you!
In postgres, a timestamp is not stored as a string like in sqllite:
select 'A' where localtimestamp like '2011-04-04%';
ERROR: operator does not exist: timestamp without time zone ~~ unknown
how about using >= instead?
select 'A' where localtimestamp >= '2011-04-04';
?column?
----------
A
(1 row)
While I liked #JackPDouglas answer, I wanted to try and keep sql code out of my project as much as possible, so i ended up doing this:
...
date_builder
if !(params[:year].nil?)
#articles = Article.where(
{:created_at.gt => #datelower} &
{:created_at.lt => (#datehigher - 1.second)} &
:genre.matches % genre_builder
).order("created_at DESC")
else
...
and this method for the date:
def date_builder
if !(params[:day].nil?)
#datelower = Time.utc(params[:year], params[:month], params[:day])
#datehigher = #datelower + 1.day
elsif !(params[:month].nil?)
#datelower = Time.utc(params[:year], params[:month], 01)
#datehigher = #datelower + 1.month
elsif !(params[:year].nil?)
#datelower = Time.utc(params[:year], 01, 01)
#datehigher = #datelower + 1.year
end
end

Converting SQL containing top, count, group and order to LINQ (2 Entities)

Some LINQ queries still puzzle me.
for a table 'Hits' containing two columns, 'Page' and 'Date', I want to find the most Pages with the most rows in a defined slice of time.
In SQL I would use this:
SELECT TOP 10
[Page]
,COUNT([Page]) as Number
FROM dbo.[Hits]
WHERE [Date] >= CONVERT(datetime,'14 Jan 2009')
AND [Date] < CONVERT(datetime,'15 Jan 2009')
Group BY [Page]
Order by Number DESC
In LINQ I got no idea how to approach this, can anyone help me here? I tried to convert it using linqer, but it just shows an error for this expression.
Something like this should work:
(from p in DataContext.Hits
where (p.Date >= minDate) && (p.Date < maxDate)
group p by p.Page into g
select new { Page = g.Key, Number = g.Count() }).OrderByDescending(x => x.Number).Take(10);
var top10hits = objectContext.Hits
.Where(h => minDate <= h.Date && h.Date < maxDate)
.GroupBy(h => h.Page)
.Select(g => new { Page = g.Key, Number = g.Count() })
.OrderByDescending(x => x.Number)
.Take(10);