I've been trying to figure this out for some time now and just can't seem to make it work. I have a table that looks similar to this.
Table: Issues
id yearly_issue year stock created_at updated_at magazine_id
1 10 2000 1 [timestamp] [timestamp] 3
2 12 1994 6 [timestamp] [timestamp] 10
3 36 2007 10 [timestamp] [timestamp] 102
4 6 2002 7 [timestamp] [timestamp] 7
5 6 2002 2 [timestamp] [timestamp] 5
6 12 2003 9 [timestamp] [timestamp] 10
7 11 2003 12 [timestamp] [timestamp] 10
My problem is that I need to sort it (easy!) but, I only want to get one of each magazine (column magazine_id).
My Eloquent query as of now is:
$issues = Issue::where('stock', ($all ? '>=' : '>'), 0)
->orderBy('year', 'desc')
->orderBy('yearly_issue', 'desc')
->take($perpage)
->get();
I thought adding the groupBy('magazine_id') would help, but it seems as though it only partially helps me. The results is not in the correct order. So, my question then is - is there any easy way around this?
I've been experimenting with various answers to similar questions but I completely fail to implement it.
Retrieving the last record in each group
or
How to get the latest record in each group using GROUP BY?
More help would be much appreciated.
EDIT:
The closest I am currently is this:
SELECT i1.*, c.name AS image, m.title AS title
FROM issues i1
INNER JOIN covers c ON i1.id = c.issue_id
INNER JOIN magazines m ON i1.magazine_id = m.id
JOIN (SELECT magazine_id, MAX(year) year, MAX(yearly_issue) yearly_issue FROM issues GROUP BY magazine_id) i2
ON i1.magazine_id = i2.magazine_id
AND i1.year = i2.year
-- AND i1.yearly_issue = i2.yearly_issue
WHERE i1.stock ($all ? '>=' : '>') 0
ORDER BY i1.year DESC, i1.yearly_issue DESC
LIMIT $perpage
However, it is not giving me the desired result at all.
You need to add a MAX function in the SELECT clause for each column to be ordered in DESCending order. The inverse goes for columns ordered in ASCending order, you need to add MIN function in the SELECT clause.
Your Eloquent query has to include a raw select:
$issues = DB::table('issues')
->select(DB::raw('id, max(year) as year, max(yearly_issue) as yearly_issue, stock, created_at, updated_at, magazine_id'))
->groupBy('magazine_id')
->orderBy('year', 'desc')
->orderBy('yearly_issue', 'desc')
->take(10)
->get();
The only drawback is that you need to specify each column you want to retrieve. And do not use the * selector, it will override the aggregate function in the select clause.
Update: Seems like adding the * selector before the aggregate functions in the SELECT clause works too. This means that you rewrite the raw select as:
->select(DB::raw('*, max(year) as year, max(yearly_issue) as yearly_issue'))
I think putting the * selector before makes the aggregate functions overrides their columns.
I was looking for a way to order results before grouping and this answer kept popping up. In my case, it was for a user's message inbox. I needed to display the latest message received by the user in each message thread they were part of. My table looks like this:
--------------------------------------------------------------------
| id | sender_id | receiver_id | message | created_at | updated_at |
--------------------------------------------------------------------
| | | | | | |
--------------------------------------------------------------------
The accepted answer on this question, groups the results and then sorts them. I needed to sort the results by the created_at field, descending, before grouping them so I'd get the latest message from each thread. Anyway, here was my final solution after a lot of digging:
$sub = $query->orderBy('created_at', 'desc');
return $query->select('*')
->from(DB::raw("({$sub->toSql()}) as sub"))
->groupBy('sender_id');
This constructs the following query:
select * from (
select * from `messages`
order by `created_at` desc
) as sub
group by `sender_id`
In MySQL you can do something like this:
select *
from `UserTable`
where userId=3
group by `field` desc
order by `dateActivityComplete` ;
In eloquent you would do something like this:
->groupBy('field','desc'), however, ordering the groupBy doesn't seem to be an option in eloquent the same as order by is.
Might be an instance where you have to use a raw query, or possibly the max function.
Wade
Related
I have 40 rows in my table, what i want is to get 20 row randomly, and after order these 5 list by id; these 2 in one query;
for example in range of id 1 to 40; i get from the random order 15 10 14 9 3;so in the final result i expect to get 3 9 10 14 15
i tried to use inRandomOrder and orderBy in same time
$query->inRandomOrder()->orderBy('id', 'asc')
but the inRandomOrder query always has the final execution of my query; and if i switch their position:
$query->orderBy('id', 'asc')->inRandomOrder()
the orderBy always has the final execution
2 solutions I guess, if you manipulate only dozens of rows, you can use Laravel collection to order by ID your query result.
Just use:
$result = $query->inRandomOrder()->get();
$resultSortedById = $result->sortBy('id');
Or using SQL query, you can use this query:
SELECT * FROM
(
SELECT * FROM table
ORDER BY RAND()
LIMIT 20
) result
ORDER BY id ASC
You can translate in Eloquent way, or as SQL directly.
I am not sure to understand your question btw: order these 5 list by id ? What do you mean exactly ?
Here i want to fetch results based on the maximum date from the field so in order to get that i wrote query like this
$latest_reports = Filelist::
select('report_type_id',DB::raw('filename,max(data_date) as latest_date'))
->where('access_id','=',$retailer_supplier_id->id)
->groupBy('report_type_id')
->orderBy('data_date','desc')
->get();
Here is my table please have a look
id access_id filename report_type_id data_date
1 16 filename1 6 2021-02-01
2 16 filename2 6 2021-01-01
3 16 filename3 6 2021-03-01
4 16 filename4 6 2021-04-01
Am getting result like this
id access_id filename report_type_id data_date
4 16 filename1 6 2021-04-01
I want to get result like this
id access_id filename report_type_id data_date
4 16 filename4 6 2021-04-01
Here the first rows filename value is getting..how to solve this
This is a MySQL problem I think. You have specified only one column to group by, but more then one column in the select list so what is presented in those other non-aggregating columns isn't guaranteed to be sensible. Please refer to MySQL Handling of GROUP BY
In SQL I might re-write the query this way:
select * from mytable
where data_date = (select max(data_date) from mytable)
or
select * from mytable
order by data_date
limit 1
depending on my particular needs (and I don't know which is better for you)
UPDATE:
$latest_reports = Filelist::select([
'report_type_id',
'access_id'
DB::raw('MAX(data_date) AS data_date'),
// here can be listed the other fields
])
->where('access_id', $retailer_supplier_id->id)
->groupBy('report_type_id')
->get();
INITIAL:
I had similar working query, just there I had "created_at" timestamp field. Anyway, I think this will work for you:
// assuming, that your table name is "filelist"
$latest_reports = Filelist::select(DB::raw('t.*'))
->from(DB::raw('(SELECT * FROM filelist ORDER BY data_date DESC) t'))
->groupBy('t.report_type_id')
->get();
Background - I'm creating a system where administrators can create arbitrary fields, which are then combined into a form. Users then complete this form, and the values input against each field are stored in a table. However, rather than overwrite the previous value, I plan on keeping each past value as individual rows in the table. I then want to be able to display the contents submitted in each form, but only the most recently submitted value.
Problem
I have a model, Service, that features a belongsToMany relationship with another model, Field. This relationship is defined as:
public function fields()
{
return $this->belongsToMany('App\Field')->withPivot('id', 'value', 'date')->withTimestamps();
}
The intermediary table has 3 values I wish to retrieve, id, value and date.
A Service may have 1 or more Fields, and for each field it may also have more than 1 pivot row. That is, a single Service/Field pairing may have multiple entries in the pivot table with different pivot values. For example:
Table field_service
id | service_id | field_id | value | created_at
------------------------------------------------------
1 | 1 | 1 | lorem | 2018-02-01
2 | 1 | 1 | ipsum | 2018-01-01
3 | 1 | 1 | dolor | 2017-12-01
4 | 1 | 2 | est | 2018-03-10
5 | 1 | 2 | sicum | 2018-03-09
6 | 1 | 2 | hoci | 2018-03-08
What I want is to get either:
A specific row from the pivot table for each Field associated with the Service, or
A specific value from the pivot table for each Field associated with the Service.
For example - in the table above, I would like the Service with ID 1 to have 2 Fields in the relationship, with each Field containing an attribute for the corresponding pivot value. The Fields attached would be specified by the corresponding pivot table entry having the most recent date. Something akin to:
$service->fields()[0]->value = "lorem"
$service->fields()[1]->value = "est"
I feel there's an obvious, 'Laravel'ly solution out there, but it eludes me...
Update
Somewhat unbelievably this is another case of me not understanding windowing functions. I asked a question 7 years ago that is basically this exactly problem, but with raw MySQL. The following raw MySQL basically gives me what I want, I just don't know how to Laravelise it:
SELECT services.name, fields.name, field_service.value, field_service.created_at, field_service.field_id
FROM field_service
INNER JOIN
(SELECT field_id, max(created_at) as ts
FROM field_service
WHERE service_id = X
GROUP BY field_id) maxt
ON (field_service.field_id = maxt.field_id and field_service.created_at = maxt.ts)
JOIN fields ON fields.id = field_service.field_id
JOIN services ON services.id = field_service.service_id
Try this:
public function fields()
{
$join = DB::table('field_service')
->select('field_id')->selectRaw('max(`created_at`) as `ts`')
->where('service_id', DB::raw($this->id))->groupBy('field_id');
$sql = '(' . $join->toSql() . ') `maxt`';
return $this->belongsToMany(Field::class)->withPivot('id', 'value', 'created_at')
->join(DB::raw($sql), function($join) {
$join->on('field_service.field_id', '=', 'maxt.field_id')
->on('field_service.created_at', '=', 'maxt.ts');
});
}
Then use it like this:
$service->fields[0]->pivot->value // "lorem"
$service->fields[1]->pivot->value // "est"
I know there is IN as alternative to multiple ORs:
select loginid from customer where code IN ('TEST1','TEST2','TEST3','TEST4'))
This will return all loginids with code that mach any of the four TEST elements.
Is there something similar for AND? I will need to find out all loginids that have code: TEST10,TESTA,TEST1,TESTB,AIFK,AICK....(there are 20 codes)
You cannot compare that. With ORs or IN you look for records that match one of the values. With AND you would look for a record where the column matches all those values, but this field can of course only hold one value, so will never find any record.
So obviously you are looking for something entirely else. Probably you want to aggregate your data, to find IDs for which records for each of the values exist. This would be:
select loginid
from customer
where code IN ('TEST1','TEST2','TEST3','TEST4')
group by loginid
having count(distinct code) = 4;
You could do the following:
SELECT loginid
FROM customer
WHERE code IN ('TEST1', ... , 'TEST20')
GROUP BY loginid
HAVING COUNT(DISTINCT code) = 20;
The difference between Jon's answer and this one is that if you use other codes in the table, my query will return all loginids for which there are rows for these 20 codes, and Jon's answer will return all loginids for which there are 20 distinct codes.
No
A short answer, I know, but sometimes it's the only real answer.
However
Depending on what exactly you're trying to achieve, you may be able to use a count of a query grouping the items, to check that all match.
This assumes that your CustomerCodes are kept in a separate relational table.
SELECT loginID
FROM Customer
WHERE loginID IN (
SELECT loginID, count(*) as codeCount
FROM CustomerCodes
GROUP BY loginID
HAVING codeCount = 20
)
It doesn't work if you have Code1, Code2 Code3 etc fields... you'd have to split the data out into a separate table, for example:
loginID | code
---------------
1 | code1
1 | code2
I need to find the total number of logins per day, but how would I select count() and group by day in DQL? I'm using Doctrine 2.3.
public function getLoginCount()
{
return $this->createQueryBuilder('i')
->select('i') // and count(*)
->groupBy('i.timestamp') // group by day
->getQuery()
->execute()
;
}
I need something like this:
Date | count
2013-01-01 | 6
2013-01-02 | 7
2013-01-03 | 3
From help on the Doctrine IRC channel you need to create a custom DQL function.
Example:
https://github.com/beberlei/DoctrineExtensions/blob/master/lib/DoctrineExtensions/Query/Mysql/Day.php
Docs:
http://www.doctrine-project.org/blog/doctrine2-custom-dql-udfs.html
A bit late for OP, but maybe someone will find it handy. I was able to achieve that with the DQL query bellow:
$dql = '
SELECT
SUBSTRING(i.timestamp, 1, 10) as date,
COUNT(i) as count
FROM Entity i
GROUP BY date
';
$query = $entityManager->createQuery($dql);
return $query->getResult();
I think similar should be doable with Doctrine query builder.