Eloquent complex query - sql

I need sql query, but i need to use Eloquent:
"SELECT * FROM Pass WHERE serial_number=$registered_serial_numbers AND (updated_at IS NULL OR updated_at >= $passesUpdatedSince)"
I try but have error:
$registered_passes = Pass::where('serial_number', $registered_serial_numbers)->where("updated_at IS NULL OR updated_at >= $passesUpdatedSince")->get();
How should i do it in Eloquent?

Two ways of doing it:
1) using whereRaw()
$registered_passes = Pass::whereIn('serial_number', $registered_serial_numbers)->whereRaw('updated_at IS NULL OR updated_at >= ?', $passesUpdatedSince)->get();
2) using a closure to do the AND (OR)
$registered_passes = Pass::whereIn('serial_number', $registered_serial_numbers)->where(function($query){
$query->whereNull('updated_at')->orWhere('updated_at', '>=', $passesUpdatedSince);})->get();
https://laravel.com/docs/5.3/queries#where-clauses
Have a look at the "Parameter Grouping" section

The query should be something like this:
$registered_passes = Pass::where("updated_at", $passesUpdatedSince)->whereIn('serial_number',$registered_serial_numbers)->get();
You use whereIn method for where in clause and make sure $registered_serial_numbers is an array of values.
My bad, misread it. If you are fine with serial number, just do a whereRaw like this:
$registered_passes = Pass::where('serial_number',$registered_serial_numbers)->whereRaw('updated_at IS NULL OR updated_at >= ?', $passesUpdatedSince)->get();

Related

Does Laravel Eloquent 'where' guarantee order?

For example, does this:
Model::where('role', 'all')->orWhere('role','user')->first();
Always get the model where role is 'all' first, because where() is declared earlier than the orWhere()?
Or should I not trust that and always check with if?
$foo = new Model;
$bar = $foo->where('role', 'all')->first();
if(empty($bar)){
$bar = $foo->where('role','user')->first();
}
Or maybe a better solution?
No it will not, and it has nothing to do with Laravel, this is just how SQL works.
The query builder will generate the following SQL statement:
SELECT * FROM Model WHERE `role` = 'all' OR `role` = 'user' LIMIT 1;
The order you will get the rows in will be the order they are in the Model table. If you want to get the rows sorted you will have to apply an order to the query, for example:
Model::where('role', 'all')
->orWhere('role','user')
->orderByRaw('CASE role WHEN \'all\' THEN 1 ELSE 2 END DESC')
->first();
You can do something like this:
Model::whereIn('role', ['all', 'user'])->get();
If you want to order results and get only first one you can specify order like this:
Model::whereIn('role', ['all', 'user'])->orderBy('some_column', 'ASC')->first();
But of course you can do it your way like this:
Model::where('role', 'all')->orWhere('role','user')->orderBy('some_column', 'ASC')->first();

OrientDB using LET values in subQuery

How can you use a LET temporary variable inside the Where clause in an OrientDB SQL subQuery.
Here is the context in wich I'm trying to use it.
select *, $t.d from Currency
let $t = (select createdDate.asLong() as d from 13:1)
where createdDate.asLong() >= $t.d and #rid <> #13:1
order by createdDate ASC
The validation in the where statement for the dates does not work. The subQuery actually works on its own. The Query works as well when replacing $t.d with the result from the subQuery.
The $t.d is an array so you are comparing something like createdDate.asLong() >= [1234599]
You have to do this: createdDate.asLong() >= $t[0].d

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

Rails 3 query in multiple date ranges

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)

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)])