Suppose we have some date ranges, for example:
ranges = [
[(12.months.ago)..(8.months.ago)],
[(7.months.ago)..(6.months.ago)],
[(5.months.ago)..(4.months.ago)],
[(3.months.ago)..(2.months.ago)],
[(1.month.ago)..(15.days.ago)]
]
and a Post model with :created_at attribute.
I want to find posts where created_at value is in this range, so the goal is to create a query like:
SELECT * FROM posts WHERE created_at
BETWEEN '2011-04-06' AND '2011-08-06' OR
BETWEEN '2011-09-06' AND '2011-10-06' OR
BETWEEN '2011-11-06' AND '2011-12-06' OR
BETWEEN '2012-01-06' AND '2012-02-06' OR
BETWEEN '2012-02-06' AND '2012-03-23';
If you have only one range like this:
range = (12.months.ago)..(8.months.ago)
we can do this query:
Post.where(:created_at => range)
and query should be:
SELECT * FROM posts WHERE created_at
BETWEEN '2011-04-06' AND '2011-08-06';
Is there a way to make this query using a notation like this Post.where(:created_at => range)?
And what is the correct way to build this query?
Thank you
It gets a little aggressive with paren, but prepare to dive down the arel rabbit hole
ranges = [
((12.months.ago)..(8.months.ago)),
((7.months.ago)..(6.months.ago)),
((5.months.ago)..(4.months.ago)),
((3.months.ago)..(2.months.ago)),
((1.month.ago)..(15.days.ago))
]
table = Post.arel_table
query = ranges.inject(table) do |sum, range|
condition = table[:created_at].in(range)
sum.class == Arel::Table ? condition : sum.or(condition)
end
Then, query.to_sql should equal
(((("sessions"."created_at" BETWEEN '2011-06-05 12:23:32.442238' AND '2011-10-05 12:23:32.442575' OR "sessions"."created_at" BETWEEN '2011-11-05 12:23:32.442772' AND '2011-12-05 12:23:32.442926') OR "sessions"."created_at" BETWEEN '2012-01-05 12:23:32.443112' AND '2012-02-05 12:23:32.443266') OR "sessions"."created_at" BETWEEN '2012-03-05 12:23:32.443449' AND '2012-04-05 12:23:32.443598') OR "sessions"."created_at" BETWEEN '2012-05-05 12:23:32.443783' AND '2012-05-21 12:23:32.443938')
And you should be able to just do Post.where(query)
EDIT
You could also do something like:
range_conditions = ranges.map{|r| table[:created_at].in(r)}
query = range_conditions.inject(range_conditions.shift, &:or)
to keep it a little more terse
I suggest you try the pure string form:
# e.g. querying those in (12.months.ago .. 8.months.ago) or in (7.months.ago .. 6.months.ago)
Post.where("(created_at <= #{12.months.ago} AND created_at >= #{8.months.ago} ) OR " +
"(created_at <= #{7.months.ago} AND created_at >= #{6.months.ago} )" )
In your case, I would suggest to use mysql IN clause
Model.where('created_at IN (?)', ranges)
Related
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
I have a report with a table in Rails where users can optionally set filters like selecting a location or picking a range of dates and update the table via an ajax request.
Can I write this where clause so that it any date/blanks or all locations?
#orders = Order.where('created_at <= ? AND ? <= created_at AND location_id = ?', date_order_start, date_order_end, loc_filter)
The query above fails on blanks (e.g., "") and if I put nils they translate to nulls in the SQL.
To solve this problem right now I have a bunch of conditional statements that check whether value is present in the ajax request and then creates a different where clause depending on the case. My current conditionals are unwieldy, error prone and not scalable.
Searches on things like "wildcard sql" end up leading me to text searches (i.e., %) which I don't think fits in this case.
I am running on Rails 3.2 with postgresql.
I sometimes use an array of query statements and arguments like this:
queries = []
args = []
if some_condition
queries.push("created_at <= ?")
args.push(whatever_date)
end
if another_condition
queries.push("created_at >= ?")
args.push(another_date)
end
#order = Order.where(queries.join(" AND "), *args)
What I basically want to do is the following:
a = Assignment.all(:conditions => { :end_date < :start_date })
I only want to select those records on which the end_date is before the start_date.
I actually don't want ending up writing a
for each, push to array if end_date is earlier than start_date.
How can I achieve this in a pretty 'Railsy'-way?
Thank you in advance.
Edit:
I think the problem is comparing the values of both columns. (Allmost) every query is comparing a cell-value to an input-value.
This is a shot in the dark, but maybe one in the right direction ?
Haven't figured out a solution.
It's been a while here but nevertheless, you can just use the ArelTable method:
t = Assignment.arel_table
Assignment.where(t[:end_date].lt(t[:start_date]))
The condition predicates documentation: http://www.rubydoc.info/github/rails/arel/Arel/Predications
The ArelTable documentation: http://www.rubydoc.info/github/rails/arel/Arel/Table
And a good guide: http://jpospisil.com/2014/06/16/the-definitive-guide-to-arel-the-sql-manager-for-ruby.html
a = Assignment.all(:conditions => ["end_date < ?", :start_date ])
Check this: http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-all
I am assuming that you are using ActiveRecord
try
a = Assignment.where('regioassignments.end_date < regioassignments.start_date')
use the tablename followed by the EXACT COLUMNNAMES in the database, since ActiveRecord recognizes this and uses this as SQL directly.
That means the columnname for end_date is probably assignment_expected_end_date from what I figured from one of your comments.
adapted from this answer
I'm trying to select the following data with the limited information. The problem is that when I have added the .select distinct section it has killed my query.
#activities = Availability.select.("DISTINCT user_id").where("team_id = ? and schedule_id = ?", current_user[:team_id], #next_game).last(5)
There's one too many dot's in there as the 'DISTINCT user_id' is the arguments for the select method call.
So:
Availability.select("DISTINCT user_id").where("team_id = ? and schedule_id = ?", current_user[:team_id], #next_game).last(5)
Also be aware that you're now only selecting one attribute and you'll get a partial representation of the classes back. To circumvent this just select the attributes you need later in the code.
Availability.select("DISTINCT(`user_id`), `team_id`").where("team_id = ? and schedule_id = ?", current_user[:team_id], #next_game).last(5)
etc.
Hope this helps.
How can I union two criteria with an OR statement?
For example I want to get Employee which has null in Birthday field OR value of this field is less than someDate. How should I rewrite this code:
var query = DetachedCriteria.For<Employee>()
.Add(SqlExpression.IsNull<Employee>(p => p.Birthday))
.Add<Employee>(emp => emp.Birthday.Value < someDate);
You need to use Disjunction()