I have the following tables:
competitions (id, ...)
questions (id, ...)
teams (id, ...)
team_user (id, user_id, team_id, ...)
answer_user (user_id, competition_id, question_id, points, ...)
I am trying to build a query to list all teams who participated in the competition id = 20, given that we sort the list based on accumulation of the teams' users' points. However, sometimes 2 teams receive the same total points, so we need to show the team who was able to reach that total of points first in the list before others (based on the answers created_at column)
I am able to get this list using the following query, but not able to sort based on answer_user created_at column:
$ranks = Team::withCount(['answers' => function ($q) use ($competition_id) {
$q->where('competition_id', $competition_id)
->select(DB::raw('sum(points)'));
}])
->where('competition_id', $competition_id)
->orderBy('answers_count', 'desc')
->get();
Edit 1
I can achieve the sort required when I replace the
->select(DB::raw('sum(points)')
with
->select(DB::raw('max(answer_user.created_at)')
However, I want actually both aggregations to work. Basically, finding the total points each team scored, then sort them based on the time, first to score is the one show first.
Please try to use sortBy() or sortByDesc() function after get()
https://laravel.com/docs/5.8/collections#method-sortbydesc
$ranks = Team::withCount(['answers'=>function($q) use($competition_id){
$q->where('competition_id',$competition_id)->select(DB::raw('sum(points)'));
}])->where('competition_id',$competition_id)->get()->sortByDesc('answer_user');
Related
I want to add the price field from the enginestable with the buying_price field from the vehicles table where we have several purchases of vehicles and several engines and I want to sum the final cost for each purchases
I have the following tables
I want to add the price field from the enginestable with the buying_price field from the vehicles table where we have several purchases of vehicles and several engines and I want to sum the final cost for each purchases
I wrote the following command, but I get an error:
$purchase=DB::table('purchases')
->leftjoin('vehicles', 'vehicles.purchase_id', '=','purchases.id')
->leftjoin('engines', 'engines.purchase_id','=','purchases.id')
->leftjoin('suppliers', 'suppliers.id', '=', 'purchases.supplier_id')
->select('purchases.id','suppliers.name','purchases.purchase_date','vehicles.buying_price','engines.price')->get()
->collect('vehicles.buying_price','engines.price')->sum();
return $purchase
this is the the error:
In short, how do I add two values from two different tables and show them in another table?
you can call back of sum instead of collect.
For example Try this:-
$purchase=DB::table('purchases')
->leftjoin('vehicles', 'vehicles.purchase_id', '=','purchases.id')
->leftjoin('engines', 'engines.purchase_id','=','purchases.id')
->leftjoin('suppliers', 'suppliers.id', '=', 'purchases.supplier_id')
->select('purchases.id','suppliers.name','purchases.purchase_date','vehicles.buying_price','engines.price')->get()
->sum(function($data){
return $data->buying_price + $data->price;
});
Hello and thank you beforehand for your help.
I've been hitting my head against a wall with this problem for a few days now so decided to ask here. I have two queries in Laravel, one grouping totals by week, and the other by month. The week one works fine but for some reason the month one doesn't, the only difference in essentially the query is that the weekly one is calculated yearly but in a different period (starting in week 48 of last year and ending in week 47 of this year), while the monthly is just the real year. The only other difference is that the week query is inside an if to show the right thata in those final weeks of the year.
$weeklySalesLastYear = Invoice::where(function ($query) use ($year, $client_ids){
$query->where('year', $year-2)->where('week', '>=', 48)->whereIn('client_id', $client_ids);
})->orWhere(function($query) use ($year, $client_ids){
$query->where('year', $year-1)->where('week', '<=', 47)->whereIn('client_id', $client_ids);
})->groupBy('week')->selectRaw('sum(total) as total, week')->get();
That is my weekly query which works perfectly.
$sortedMonthlySalesLastYear = DB::table('invoices')
->where('year', $year-1)->whereIn('client_id', $client_ids)
->groupBy('month')->selectRaw('sum(total) as total, month')->get();
And this is my monthly query which doesn't work. I know that there is an issue with whereIn clauses in eloquent where they don't accept a big number of elements for some reason, but I'm wondering why one works and not the other one and if there is a solution to it. I also want it to be an object, I've tried using a raw query but it throws an array instead, and I would rather avoid using that. This is the one that worked.
$sortedMonthlySalesLastYear = DB::select( DB::raw("SELECT SUM(total) AS total, month FROM invoices WHERE year = '$lastYear' AND client_id IN ($client_ids_query) GROUP BY month"))
Schema::create('invoices', function (Blueprint $table) {
$table->id();
$table->string('month');
$table->integer('year');
$table->integer('week');
$table->integer('client_id')->index()->unsigned();
$table->integer('product_id')->index()->unsigned();
$table->integer('quantity');
$table->float('total');
$table->double('discount');
});
This is what my invoices migration looks like, the client relates to the user and that's how I get the arrays.
This is what the monthly query returns:
[2022-05-02 23:40:05] local.INFO: monthly sales:
[2022-05-02 23:40:05] local.INFO: []
And this is what the weekly one returns (it's a larger set
but this is a sample of what it throws to show its working.)
[2022-05-02 23:42:42] local.INFO: weekly sales:
[2022-05-02 23:42:42] local.INFO:
[{"total":536190.4699999997,"week":1},
{"total":568192.6700000003,"week":2},
{"total":1613808.48,"week":3},
{"total":878447.3600000001,"week":4}...]
An example of a few invoices I'm trying to process is this (there are more than 130K invoices in the database):
I'd appreciate any help and if you have a solution to this, I mostly just prefer to stay using eloquent for the code to look cleaner. Thank you.
I also have to add that the query returns the expected values if I sign in with any other user since the range of clients they have is much smaller.
I figured it out after so long. The only thing I did was implode the client_ids collection and then explode it into an array. No idea why it does accept a big array and not a big collection, and still no idea about the discrepancy between the queries.
$clients = Client::where('user_id', $user_id)->get('id');
$imp = $clients->implode('id', ', ');
$client_ids = explode(', ', $imp);
All queries work with that.
I'm trying to get duplicates with two conditions :
the standard groupBy method (by name and type)
and also by similarities on the name field.
In all articles about this, they always find similarities from a string, but here i want to compare with all others records on the same table.
So far I have tried the following query which retrieves the distance well but does not allow to group the results on the distance :
ERROR: window functions are not allowed in HAVING LINE
$query->select('name', 'type', DB::raw('COUNT(*) as count'), DB::raw('LEVENSHTEIN(UPPER(name),UPPER(lag(name) OVER (order by name))) as distance'))
->groupBy('name', 'type')
->havingRaw('COUNT(*) > 1')
->orHavingRaw('LEVENSHTEIN(UPPER(name),UPPER(lag(name) OVER (order by name))) > 20');
How to get duplicates using the levenshtein distance on the same field over all the table (postgresql)?
I have a Laravel 4.2 project where I get data from a SQL DB and I can display onto the page. I can select the single record just fine, but I want to also show the records around the one selected.
For example, I want to show the 5 records above and below the one selected. Im not sure how to do this in Laravel.
$gradschoolrange = MOGRadschool::where('Title', '=', $gradschool)->get();
In the above example $gradschool might be "Test College", it will return that with a value, but I want to show all the other related records around it with those values too. The results should look something like this:
ABC College
Another College
Blah College
Go To College
Test College
Yet Another College
Yo Yo College
College College
Something College
Eating College
As there's no ordering specified in your initial query, I'm assuming you want 5 next/previous records according to primary key (id? - if not, you would obviously need to change that) in the table?
Given that IDs may not be numerically sequential, we can't simply assume that the previous 5 rows will be the ID of the row with title = $gradschool minus 5, so wondered if this might work:
$initial = MOGRadschool::where('Title', $gradschool)->first(); // get the initial row with the title of $gradschool
$result = MOGRadschool::where('id', '<', $initial->id)->take(5)->orderBy('id', 'DESC') // new query getting the previous 5 rows, by ID
->union(MOGRadschool::where('id', '>', $initial->id)->take(5)) // union a second query getting the next 5 rows by ID
->get() // get the result as a collection
->add($initial) // add the initial row to the collection
->sort(); // sort the collection (by id) so that the initial row is in the middle
So the output is a collection containing the initial row in the middle, with up to 5 records either side. You also have the initial row to highlight the output, if you need that.
If you want it based on the IDs, which is what I understand from your issue, something like this should work:
$selectedGradSchool = MOGRadschool::where('Title', '=', $gradschool)->get()->first();
$aboveSelected = MOGRadschool::where('id', '<=', $selectedGradSchool->id)
->orderBy('id', 'desc')
->take('5')
->get();
$belowSelected = MOGRadschool::where('id', '>' $selectedgradSchool->id)
->take('5')
->get();
//Concatenate both results
$schoolRange = $aboveSelected->concat($belowSelected);
Now the collection should look similar to your desired result.
I have 2 models; Post and Rating
The Rating model contains an amount column which specifies how high something has been rated. This is based on 5 star rating so the amount can be a value from 1-5
The Post model has a one to many relation with the rating model and function called Ratings that returns the hasMany.
I'd like to get the 5 latest posts based on the average rating. For the average rating I've created a function that can be seen below
Note: the plural(Ratings) returns the hasMany relation where as the singular(Rating) returns a value which is the average rating
public function Rating(){
return floor($this->Ratings()->avg('rating'));
}
Is it possible to retrieve posts ordered by the avg rating using the Eloquent QueryBuilder?
Currently I'm retrieving all posts and then using the sortBy method on the collection object in order get the ones with the highest average rating. The way I'm doing this can be seen below.
$posts = Post::all();
$posts = $posts->sortByDesc(function ($post, $key) {
return $post->Rating();
});
Now if I'd only want to show 5 I still have to retrieve and sort everything which doesn't seem very resource friendly(In my eyes. I don't have any proof of this or say it is true).
So my question is the following: Is this doable using Eloquent instead of sorting the FULL collection.
Sub question: Will doing this with Eloquent instead of sorting the collection have any impact on efficiency?
You may use query builder
DB::table('post')
->select('post.id', 'AVG(rating.amount)')
->join('rating', 'post.id', '=', 'rating.post_id')
->groupBy('post.id')
->orderByRaw('AVG(rating.amount) DESC');