I'm wondering if it is possible to take the sum of multiple fields in one query using the fluent query builder.
I currently have two tables: events and attendees. Attendees belong to events and have two fields: total_raised and total_hours. What I want to do is select all events and the total amount raised/total number of hours spent on that event. Now, if I were just using SQL I would do something to the effect of:
SELECT Event.id, sum(Attendees.total_raised), sum(Attendees.total_hours)
FROM Events JOIN Attendees ON Events.id = Attendees.event_id
GROUP BY Event.id
However, I can't seem to find a way to take multiple sums at once using the fluent query builder. Is there any way to do what I'm trying to do using fluent, or should I just make it a raw SQL query?
You can use sum() i.e.:
$q = DB::table('events')
->join('attendees', 'events.id', '=', 'attendees.event_id')
->sum('total_raised')
->sum('total_hours');
If that doesn't work you can try:
...
->get(
array(
'events.id',
DB::raw('SUM(attendees.total_raised)'),
DB::raw('SUM(attendees.total_hours)')
)
);
Building on simones answer. You could do this by essentially running two queries.
$query = DB::table('events')->join('attendees', 'events.id', '=', 'attendees.event_id');
$raised = $query->sum( 'total_raised' );
$hours = $query->sum( 'total_hours' );
It depends on the situation. If it were on the admin/CMS side of things I'd be lean towards this solution. If it is on the front end it should be done in a single query which will be faster. Depending on the content it may or may not be a significant difference.
$result = DB::table('events')->join('attendees', 'events.id', '=', 'attendees.event_id')
->get( array(
DB::raw( 'SUM(attendees.total_raised) AS raised' ),
DB::raw( 'SUM(attendees.total_hours) AS hours' ),
));
I am writing this answer to help those who are in search to sum multiple fields in a single table.
If you want to sum multiple fields inside a single table so there would be no need to "join" you can simply do it likewise, assuming the table like this.
In your controller do this:
$billInfo= Bills::where('reports_id',2)->get( array(
DB::raw('SUM(Price) as total_price'),
DB::raw('SUM(balance) as total_balance'),
DB::raw('SUM(paid) as total_paid'),
));
this will result the below data:
[{"total_price":17500,"total_balance":17500,"total_paid":null}]
I am doing the same thing in my project, Here is the solution which I found. I am using Laravel 5.2 Eloquent here is the Eloquent statement.
This statement which I use in my project, Please made change according to your need.
$result = self::select("*", DB::raw('SUM(auction_amount) as total_auction_amount') , DB::raw('SUM(commission_amount) as total_commission_amount'),
DB::raw('SUM(deposit_amount) as total_deposit_amount'))
->groupBy('cp_user_id')
->get()
->toArray();
Same way you can use for your query like
$result = self::select("*", DB::raw('SUM(auction_amount) as total_auction_amount') , DB::raw('SUM(Attendees.total_raised) as total_raised'),
DB::raw('SUM(Attendees.total_hours) as total_hours'))
->with('Attendees')
->groupBy('id')
->get()
->toArray();
Related
I want to use count and sum together on DB Query not sure how to go about it. I've tried several combinations but keep getting an error. I know I can just use a raw query but would like to learn how to use it correctly
Working:
DB::select('SELECT count(*) AS order_count, sum(total_including_vat)
AS orders_total FROM orders WHERE user_id =' .$userProfile->id);
Not Working
DB::table('orders')->where('user_id', '=', $userProfile->id)->count()->Sum();
count same as aggregates returns single value so
// you try to call method sum on number and it fails
DB::table('orders')->where('user_id', $userProfile->id)->count()->sum();
you can make two requests to get sum and count but its not a good idea, or get data in collection and let it do the math
// not a good idea
//$count = DB::table('orders')->where('user_id', $userProfile->id)->count();
//$sum = DB::table('orders')->where('user_id', $userProfile->id)->sum('total_including_vat');
//collection way
$orders = DB::table('orders')
->where('user_id',$userPorfile->id)
->get(['id', 'total_including_vat']);
$result = [
'order_count' => $orders->count(),
'orders_total' => $orders->sum('total_including_vat')
];
or the same result as for your working example with mix raw expressions
$result = DB::table('orders')
->where('user_id', $userProfile->id)
->selectRaw('count(1) as order_count, sum(total_including_vat) as orders_total')
->first();
SQL query:
SELECT * FROM bd.group
INNER JOIN bd.account2group ON bd.group.id = bd.account2group.group_id
INNER JOIN bd.account ON bd.account2group.account_id = bd.account.id
WHERE bd.group.id = 8
How will it be in yii2?
There are multiple ways to get to this query using Yii2. You should probably be able to get to all these solutions by reading the docs. At https://www.yiiframework.com/doc/guide/2.0/en the 'Working with databases' should give you more than enough examples to get at least something working.
If you want to use the QueryBuilder from Yii2 it will look like this:
$query = (new \yii\db\Query)
->from('bd.group')
->innerJoin('bd.account2group', 'bd.group.id = bd.account2group.group_id')
->innerJoin('bd.account', 'bd.account2group.account_id = bd.account.id')
->where([
'bd.group.id' => 8,
]);
Then you can call $query->all(), $query->one(), or one of the other functions that the Query class holds. Check out https://www.yiiframework.com/doc/api/2.0/yii-db-query for all possible options.
I have a table called gk and I am currently running two queries. Please have a look at the queries:
Gk::groupBy(DB::raw("MONTH(created_at)"))
->groupBy(DB::raw("YEAR(created_at)"))
->selectRaw('id, user_id, sum(ton) as ton,pl, count(id) as total, sum(w) , created_at')
->with(array('user'=> function($q){
$q->select('id', 'userName', 'profilePic');
}))
->where('user_id', $userData[0]->id)
->get();
This query returns a little summary of every months. As you can I see I am grouping results by months and years. And I have another query which will return all the rows of any given months.
I am running second query like this
$m=Carbon::parse($request->date);
Gk::where('user_id',$request->user_id)->whereRaw(DB::raw("YEAR(created_at)=$m->year"))->whereRaw(DB::raw("MONTH(created_at)=$m->month"))
->orderBy('created_at','desc')
->get();
The second query returns all the rows of any month. I'm executing this query in a foreach loop for all of the months that are returned in the first query.
I am trying to combine this two query into one so that I can get a group of the results by months and years and also all the details of that month.
Any help, suggestions or idea would be extremely helpful.
[Note: For the date in second query, this date is created_at result from the first query.]
Thank you.
The way I read your question is as following: The second query is executed in a loop with results from the first one. Is that right? In my answer I have explained a way to execute the second query just one time instead of in a loop. You'd still have to execute the first query once.
So, I think that you are better of using the Php collection methods:
$results = Gk::where('user_id',$request->user_id)
->orderBy('created_at','desc')
->get()
->groupBy(function (Gk $item) {
return $item->created_at->format('Y-m');
});
The groupBy method has to return an attribute on which you want to group the elements. For this example I think that using a yyyy-mm format will do fine.
Reference: https://laravel.com/docs/5.5/collections#method-groupby
Edit: Maybe you can also get rid of the orderBy method call because you are grouping by afterwards:
$results = Gk::where('user_id',$request->user_id)
->get()
->groupBy(function (Gk $item) {
return $item->created_at->format('Y-m');
});
Edit 2: To combine the information of the two queries, you could do something like the following:
$results = Gk::where('user_id',$request->user_id)
->get()
->groupBy(function (Gk $item) {
return $item->created_at->format('Y-m');
})->map(function(Collection $rows) {
return [
'sum(ton)' => $rows->sum('ton'),
'total' => $rows->count(),
'sum(w)' => $rows->sum('w'),
'rows' => $rows
];
);
Note that I have omitted a few of the selected columns in your first query because they are not unique in the given group by. But feel free to add any logic in the map function. Because we use map() after groupBy, every call of map() will receive a collection of items for one month. You can that use that fabulous collection magic to calculate all values that you need and reduce the number of queries to just one.
I have two tables ad_pool and advertisment. ad_pool has some data while advertisment is empty. I am using this code to select values from the first table by not equal query like this andWhere(['<>','fisttablekey','second_tbl_key']). This is the complete code i am using to retrieve the data and i also uploaded the image.
$pool1 = (new Query())>select('p.id,p.cleaner_user_id,p.ad_place_id')
->from('ad_pool p')
->innerJoin('advertisment a' , 'p.id = a.pool_id')
->where(['=','ad_place_id',1])
->andWhere(['<>','p.id','a.pool_id'])
->orderBy(new Expression('rand()'))
// ->limit(1)
->all();
var_dump($pool1);
exit();
this return me Empty array. need your kind help. Thanks in advance.
The INNER JOIN keyword selects records that have matching values in both tables.
Since your advertisement is empty, it won't return any data. You can use LEFT JOIN instead.
$pool1 = (new Query())>select('p.id,p.cleaner_user_id,p.ad_place_id')
->from('ad_pool p')
->leftJoin('advertisment a' , 'p.id = a.pool_id')
->where(['=','ad_place_id',1])
->andWhere(['<>','p.id','a.pool_id'])
->orderBy(new Expression('rand()'))
->all();
W3Schools Reference
This way I solved this problem after 3 to 4 hours hardwork. Get the required values from the second table if there is any and then convert it into single array and used it in the NOT IN condition. (we must have passion of helping others and we should help each other).
Of course I can use order_by with columns in my first table but not with columns on second table because results are partial.
If I use 'join' everything works perfect but I need to achieve this in eloquent. Am I doing something wrong?
This is an example:
//with join
$data = DB::table('odt')
->join('hdt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('odt.odt as odt','hdt.servicio as servicio'));
foreach($data as $v){
echo $v->odt.' - '.$v->servicio.'<br>';
}
echo '<br><br>';
//with eloquent
$data = Odt::get();
foreach($data as $odt){
foreach($odt->hdt()->order_by('servicio')->get() as $hdt){
echo $odt->odt.' - '.$hdt->servicio.'<br>';
}
}
In your model you will need to explicitly tell the relation to sort by that field.
So in your odt model add this:
public function hdt() {
return $this->has_many('hdt')->order_by('servicio', 'ASC');
}
This will allow the second table to be sorted when using this relation, and you wont need the order_by line in your Fluent join statement.
I would advise against including the order by in the relational method as codivist suggested. The method you had laid is functionally identical to codivist suggestion.
The difference between the two solutions is that in the first, you are ordering odt ( all results ) by hdt.servicio. In the second you are retrieving odt in it's natural order, then ordering each odt's contained hdt by servico.
The second solution is also much less efficient because you are making one query to pull all odt, then an additional query for each odt to pull it's hdts. Check the profiler. Considering your initial query and that you are only retrieving one column, would something like this work?
HDT::where( 'odt_id', '>', 0 )->order_by( 'servico' )->get('servico');
Now I see it was something simple! I have to do the query on the second table and get contents of the first table using the function odt() witch establish the relation "belongs_to"
//solution
$data = Hdt::order_by('servicio')->get();
foreach($data as $hdt){
echo $hdt->odt->odt.' - '.$hdt->servicio.'<br>';
}
The simple answer is:
$data = Odt::join('hdt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('odt.odt as odt','hdt.servicio as servicio'));
Anything you can do with Fluent you can also do with Eloquent. If your goal is to retrieve hdts with their odts tho, I would recommend the inverse query for improved readability:
$data = Hdt::join('odt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('hdt.servicio as servicio', 'odt.odt as odt'));
Both of these do exactly the same.
To explain why this works:
Whenever you call static methods like Posts::where(...), Eloquent will return a Fluent query for you, exactly the same as DB::table('posts')->where(...). This gives you flexibility to build whichever queries you like. Here's an example:
// Retrieves last 10 posts by Johnny within Laravel category
$posts = Posts::join('authors', 'authors.id', '=', 'posts.author_id')
->join('categories', 'categories.id', '=', 'posts.category_id')
->where('authors.username', '=', 'johnny')
->where('categories.name', '=', 'laravel')
->order_by('posts.created_at', 'DESC')
->take(10)
->get('posts.*');