In my laravel app I have a table with 2 relevant fields:
updated_at timestamp of last review
n number of days until next review.
I am trying to write a query that returns records which are due for review. In PHP, I would do something like this:
updated_at->addDays(n) > Carbon::now()
Looking at the query builder docs, I am thinking something like the subquery where clause would be helpful, but am unsure how to approach it. Particularly as I need to use a field from the table in both sides of the where.
->where('updated_at', '>', function ($query) {
//
})
Related
I have the following tables:
class Contract
has_many :working_days
end
Class WorkingDay
belongs_to :contract
end
The working_days table has a date field.
I also have an array of dates, for example:
dates_array = [Date.today, Date.today + 1.week, Date.today + 2.weeks, Date.today + 3.weeks, Date.today + 4.weeks]
As fast as possible, and within one Activerecord or SQL query, how can I return all contracts that have a working day for all of the dates within the dates array?
I want this to be as fast as possible, so I don't want to loop over the dates array and run multiple queries.
If it was possible to map an SQL query from the dates_array that could be run within an Activerecord query, that would be ok. Something like:
Contract.joins(:working_days).where(working_days: dates_array.map { |date| "('date=?','#{date}') #{'OR' unless date == dates_array.last}" }.join(''))
But this code doesn't work, and so far I haven't been able to come up with anything that does, apart from looping over the array of dates (too slow).
Within one query, does anyone know how I can return all contracts that have a working day for all of the dates within the dates array?
Thank you to anyone who can help!
You can get all the contract records having working_days, where the working_days have exactly those dates that dates_array holds, where the total of working_days is exactly the same number of elements in dates_array, while grouping the contract rows by id:
Contract
.joins(:working_days)
.where(working_days: { date: dates_array })
.group(:id)
.having('COUNT(*) = ?', dates_array.size)
I'm using Laravel query builder and I would like to ask how specify which row to use for the left join if many rows are returned?
I have code like that:
$builder->leftJoin('table2', function (JoinClause $join) {
$join->on('table2.id', '=', "table1.id")
->on('table2.region', '=', "table1.region")
->orderBy('table2.updatedAt', 'DESC') // This is being ignored.
->limit(1);
});
The part $join->on()->on() returns multiple rows so I need to specify which one to use for the left join. I though I can order it by table2.updatedAt and then it will use the first row for the left join. But it is completly ignoring the orderBy. So I dont know how to get the one specific row.
Edit:
Also its possible that some of the rows from table2 have exactly same table2.updatedAt. In that case it does not matter which of the ones with same table2.updatedAt will be selected.
Checking the query in laravel, I've found that applying ORDER BY doesn't apply any clause to the query.
Reading your requirements, you want to achieve the record with the largest updatedAt field for the criteria specified. I would personally opt for a MAX() query in a view if permitted, but views can be restricted or not used by organisation, and its good to know how to do it in line with your question, plus it means you dont have to maintain a view. Optionally, you can do a MAX() in a subquery, but that requires a bit more setup.
First, you inner join on the fields you require. Second, you do a left join to look for values within that join that are bigger than the one you want the MAX for. So we want the most recent updatedAt field. Finally, you WHERE the largest updatedAt value has no value bigger than it, so its the true max: where that value is NULL.
Code is remixed without the leftJoin clause object, Im passing your builder everywhere. I cant see the rest of your query, but I think this should drop straight in since were only up to 2 tables ie table2. If you supply 1 table.id in the WHERE clause, you will get 1 row back.
Update: You need to do the same with Id, so if you have 2 datetimes with the same timestamp, you pick the one with the higher ID:
$sql->join('table2', function ($sql) {
$sql->on('table2.id', '=', "table1.id")
->on('table2.region', '=', "table1.region");
});
$builder->leftJoin('table2 AS biggerThanUpdatedAt', function ($builder) {
$builder->on('table2.id', '=', "biggerThanUpdatedAt.id")
->on('table2.region', '=', "biggerThanUpdatedAt.region")
->on('table2.updatedAt', '<', 'biggerThanUpdatedAt.updatedAt');
});
$builder->leftJoin('table2 AS biggerThanId', function ($builder) {
$builder->on('table2.id', '<', "biggerThanId.id") //<-notice the greater at is here this time.
->on('table2.region', '=', "biggerThanId.region")
->on('table2.updatedAt', '=', 'biggerThanId.updatedAt');
});
$builder->whereNull('biggerThanUpdatedAt.updatedAt');
$builder->whereNull('biggerThanId.id');
I have a query that returns some dates which are not in any order. I need to select the last row from the sub query. The problem is all the solutions I can find online uses something like
ORDER BY qry_doc_dates.arrival_date DESC LIMIT 1
Select qry_doc_dates.arrival_date
FROM (qry_doc_date) AS qry_doc_dates
ORDER BY qry_doc_dates.arrival_date DESC
LIMIT 1
which will not serve my purpose because it first orders the dates as DESC(or ASC).
Suppose the qry_doc_date returns :
"2019-05-27",
"2019-05-13",
"2019-05-20",
"2019-05-22",
"2019-07-12",
"2019-05-22",
"2019-07-16",
"2019-05-22"
As we can see that the returned values are not in order. If I use
ORDER BY qry_doc_dates.arrival_date DESC LIMIT 1
then it returns "2019-07-16" But I need "2019-05-22" which is the last row.
EDIT 1:
I am trying to convert this VBA query to MYSQL.
DLast("arrival_date", "qry_doc_date", "[package_id] = " & Me!lstPackage)
I suppose I misunderstood what the VBA query wants to return. Another issue is I do not have means to run this VBA query and check the result myself.
Your question doesn't make too much sense according to the SQL standard. In the absense of an ORDER BY clause the database engine is free to return the rows in any order. This order may even change over time.
So essentially you are requesting the "last random row" the query returns. If this is the case, why don't you get the "first random row"? It doesn't make any difference, does it?
The only way of getting the last random row is to get them all and discard all of them except for the last one.
Now, if you just need one random row, I would suggest you just get the first random row, and problem solved.
In response to the additional information from your edit:
EDIT 1: I am trying to convert this VBA query to MYSQL.
DLast("arrival_date", "qry_doc_date", "[package_id] = " & Me!lstPackage)
I suppose I misunderstood what the VBA query wants to return. Another
issue is I do not have means to run this VBA query and check the
result myself.
Unless your dataset qry_doc_date is ordered by means of an order by clause, the DFirst or DLast domain aggregate functions will return essentially a random record.
This is stated in the MS Access Documentation for these two functions:
You can use the DFirst and DLast functions to return a random record from a particular field in a table or query when you simply need any value from that field.
[ ... ]
If you want to return the first or last record in a set of records (a domain), you should create a query sorted as either ascending or descending and set the TopValues property to 1. For more information, see the TopValues property topic. From a Visual Basic for Applications (VBA) module, you can also create an ADO Recordset object and use the MoveFirst or MoveLast method to return the first or last record in a set of records.
What you need is to in qry_doc_date to include a sequential row number.
Then you can use something like this:
ORDER BY qry_doc_dates.row_number DESC LIMIT 1
I have a model in my project which has several fields like id, name, due_year, due_quarter, etc. Here is my table
The issue is that I want to get that records where the field "due_year" is greater than 2017 and at the same time, the field "due_quarter" must be less than 4. In simple words, I am trying to get unexpired records. I tried to build a query like this:
$rows = Model::find()
->andFilterWhere(['>=', 'due_year', date('Y')])
->andFilterWhere(['<', 'due_quarter', 4])
->all();
but unfortunately i got expired rows as well.
Thank you in advance
I need to get the number of records created in a specific period of time. What I'm trying to use right now looks kinda like this:
User.where('created_at between ? and ?', start, finish).group("date(created_at)").count
What i get is hash with dates as keys and numbers as values. BUT when a value is 0 for a specific day, it is not included in the hash. how can i include these dates as well? I mean MySQL, NOT Ruby language, i want it to be as fast as possible.
AR or MySQL can not create the group if there are no records in DB within the given range.
I had a similar issue and I only was able to solve it with Ruby..
User
.where('created_at between ? and ?', start, finish)
.group("date(created_at)")
.flat_map{ |a,b| [a => b.count] }
.inject(:merge)