Query Laravel Result Set - php

I am looking to do several queries using eloquent which are all to be returned to a single view and was wondering if there was a better way to do it than querying the database multiple times?
For example returning all the records then pulling sub sets of data from that?
At the moment I have something similar to:
$one = User::queryOne();
$two = User::queryTwo();
$three = User::queryThree();
etc
However I was thinking it would be better if it was possible to do something like:
$users = User::all();
$one = $users->where('created_at')...
$two = $users->where('modified_at')..
Obviously the above doesn't work but it it possible to do something like this?
Or is it best just to query the database separately each time?

From a pragmatic point of view, it's 'better' to do multiple queries, because it takes you less time to write them, and they are easier to read and debug. If you want to do it with one DB query, and then grabbing subsets out of them, you'd have to write your own convoluted logic, or perhaps a new Laravel collection class. And then someone else comes along and wonders, "What is going on in this code?"
Typically programmer time is the most constrained resource in a project. When you get done, if you find that the multiple database queries are a bottleneck, then think about re-writing it.
If you do decide to do one query, you can probably order the data by the fields you want for the criteria. Then loop through the result set, adding the rows to a new array each time the specified field's value changes. That's the simplest logic I can think of offhand.

What version of laravel are you using? In 5.1 you can do where on collections here. In 4.2 you can do so with callbacks here
EDIT
for 4.2 try something similar to this
$users = User::all();
$one = $users->filter(function($user)
{
if($user->age > 20){
return true;
}
return false;
});

Laravel Eloquent returns a Collection as a result.
You could use a foreach statement or use the build in Collections functions you could manipulate the results and create the sub-results.
For example you could use filter and do something like this:
$users = User::all();
$one = $collection->filter(function ($item) {
return $item->created_at >= time() - (24*60*60); // created the last day
});
$filtered->all();
Whether it is the best method depends on the application and the amount of data you are trying to fetch/process.
If you have only a few records from ::all(), then doing so might be a good approach (although using the collections functions you have to run three filters across your data).
If you have a lot of records from ::all() then it is preferably to use three different queries to the database (especially if the results will only be a few records).

Related

Laravel Query efficiency difference

Please could somebody tell me which one is most efficient select in Laravel:
$car = Car::all(); ------- $car = Car::find();
$car = DB::table('car')->get(); ------ $car = DB::table('car')->first();
Your first approach:
$car = Car::all(); ------- $car = Car::find();
Makes use of Eloquent. This means, all the rows received from the query will be hydrated to Model instances, and all of those will be injected into an instance of Collection (for multiple elements, of course). This is useful because you then will have all the benefits that this brings. However, this comes with a little decrease on performance (understandable)
Your second one:
$car = DB::table('car')->get(); ------ $car = DB::table('car')->first();
Uses the Query Builder instead. The results (as a whole) will be also casted into an instance of Collection, but its items will be simple arrays. This means that the process will be faster (more performant) but on detriment of not having all the cool features of Eloquent.
There's even a more performant option: Using raw queries. That also has tradeoffs: Results are not hydrated into a Collection instance.
Which one to use? It depends on your needs. Usually I go for the Eloquent option. I use the Query Builder directly when I need to make queries to big databases and need speed.
For me most efficient is selecting from the Model: like Car:all(), but it's always better if you use pagination or just don't take all of the records from the database with all() method.
But selecting with DB is a bit faster and in some cases maybe it would be better to use.
In the end, it always depends on what`s your problem and which way do you want to solve it.
for a better understanding I recommend you to watch this video and after that maybe keep going to search for some more information or just try it out yourself.
https://www.youtube.com/watch?v=uVsY_OXRq5o&t=205s

How to sort a Cakephp query using a post calculated field and pass it to the paginate?

Have a formatResults callback function that adds a "custom calculated" field into the entities post returned from a model query in my Cakephp. I would like to sort by this field and use this on a paginate is this possible?
So far i cannot accomplish this because the paginate limits the records fetched and therefore only records less than the paginator limit get sorted and not all the resultset...
Current code:
$owners = $this->Owners->find('all');
$owners->formatResults(function (\Cake\Collection\CollectionInterface $owners) {
$owners = $owners->map(function ($entity) {
$entity->random = rand(0,1);
return $entity;
});
return $owners->sortBy(function ($item){
return $item->random;
},SORT_DESC);
});
This works as expected:
$owners->toArray();
This does not:
$owners = $this->paginate($owners);
$owners->toArray();
Mainly because its "callback processing" only the first 10 records, i would like to process the whole resultset.
After diggin around ive found a similar topic opened by a previous user on the this link, it seems that is not possible to use pagination sort in other than the fields in the database.
As a result, i would suggest:
1 - Either alter your model logic, to accommodate your requirements by creating virtual fields or alter database schema to include this data.
2 - If the data requires further or live processing and it cannot be added or calculated in the database, perhaps programming a component that will replicate the paginate functionality on a cakephp collection would be a good option.The downside of this approach is that all records will be returned from the database which may present performance issues on large resultsets.

Will I get better performance if I use COUNT query instead of looping through entities in Symfony 4?

For example I need to get review count, one way of doing it is like this:
public function getActiveReviews()
{
return $this->getReviews()->filter(function(Review $review) {
return $review->isActive() && !$review->isDeleted();
})->count();
}
Another way is to use Query Builder like this:
$qb = $this->createQueryBuilder('r')
->where('r.active = true')
->andWhere('r.deleted = false')
->select('count(r)')
Which way will give me better performance and why?
Of course count query will be faster because it will result into single SQL query that will return single value.
Iteration over entities will require:
Run of SQL query for fetching data rows
Actual fetching of data
Entity objects instantiation and persisting fetched data into them
Depending on amount of affected data difference may be very big.
The only case when running count over entities may be fast enough is a case when you already have all entities fetched and just need to count them.
It depends on Symfony count() implementation, but you probably will. Usually RDBMS counts its rows quicker internally, and it requires much less resources.
In first case you request a whole rowset, which can be huge, then you iterate through it, you apply your filter function to every row, and then you just look at your filtered rowset size and drop everything. (But, of course, this might be optimized by your framework somehow).
In second case you just ask the database how much rows it has satisfying the criteria. And DB returns you a number, and that's all.
As other people said, the only case when first choice might be quicker is when you have already cached rowset (no need to connect to DB) — and when your DB connection is very slow at the same time.
I saw databases which were slow on some COUNT requests (Oracle) on big tables, but they were still faster than PHP code on same rowset. DBs are optimized for data filtering and counting. And usually COUNT request are very fast.

Get data from one table using one query and filter different rows into separate variables (Laravel 5.1)

I understand the question might not be very clear but here is my situation. I'm using laravel 5 and i'm developing a CRM system. I have placed Marital Status and Sex/Gender into one Lookup table. Whenever i get the values from the database and pass it to the view, i have two separate queries.
$sexes = DB::table('Lookups')
->where('ValueType', '=', 'Sex')->get();`
$marstatus = DB::table('Lookups')
->where('ValueType', '=', 'Marital Status')->get();`
return view('clients.edit',compact('client'))
->with('sexes', $sexes)
->with('marstatus ', $marstatus );
This code actually works and i am able to get both the marital status and sex/gender on my view.
So, here is my question
Doesn't this mean that i am sending a query to the database twice which affects performance even if it is small
Isn't there a way to query all the values from the lookup table in one query and filter out the values on the controller. So it can be something like
$Lookups = DB::table('Lookups')
and then filter the $Lookups variable and assign it into two different variables ($sexes and $marstatus) based on my filter criteria. i.e ($sexes is for values that have ValueType = 'Sex' ...)
Which one is better for performance. Sending a query twice or three times or just filtering the data on the controller.
1) Yes it does. Just install Laravel Debugbar and see it yourself. It's a very handy tool strongly recommended.
2) Yes you can do that, laravel has nice helper functions for that type of needs:
$collection = collect(DB::table('Lookups')
->whereIn('ValueType', ['Marital Status', 'Sex'])
->get());
$marstatus = $collection->filter(function($item) {
return $item->ValueType == 'Marital Status';
});
$sexes = $collection->filter(function($item) {
return $item->ValueType == 'Sexes';
});
What this does is, it converts the result array to a Laravel Collection so that you can use the filter function. You can also use array_filter function to filter without converting the result array to a collection.
3) Databases are always one of the primary bottlenecks, the fewer the query number the better. However this should not be a general rule especially when cache is used. And for example making joins or subqueries to reduce the number of queries would be deadly mistake on some cases.
Performance is a huge subject. I'd recommend you to start with the Laravel Debugbar to compare the memory usage, number of queries etc. and investigate more on various techniques including cacheing and design patterns too. Accessing the tables directly within the controller is not a very good idea in the first place...
Yes it does mean that. How big is your Lookups table?
You probably mean $lookups = DB::table('Lookups')->all(); or perhaps consider using an Eloquent model class instead, e.g. $lookups = Lookup::all(); Perhaps you may want to cache the result if the table is small? e.g. use the Cache classes in Laravel.
Better for performance would be to use the cache.
It is belong to your query Data I mean your lookups table data.
You can write the query like this in one time:
$sexes_marital_status= DB::table('Lookups')->where('ValueType', '=', 'Sex')
->orWhere('ValueType' '=', 'Marital Status' )
->get();
return view('clients.edit',compact('client'))
->with('sexes_marital_status',$sexes_marital_status);
and this is better that you send your query in one time.
`

Laravel query builder - re-use query with amended where statement

My application dynamically builds and runs complex queries to generate reports. In some instances I need to get multiple, somewhat arbitrary date ranges, with all other parameters the same.
So my code builds the query with a bunch of joins, wheres, sorts, limits etc and then runs the query. What I then want to do is jump into the Builder object and change the where clauses which define the date range to be queried.
So far, I have made it so that the date range is setup before any other wheres and then tried to manually change the value in the relevant attribute of the wheres array. Like this;
$this->data_qry->wheres[0]['value'] = $new_from_date;
$this->data_qry->wheres[1]['value'] = $new_to_date;
Then I do (having already done it once already)
$this->data_qry->get();
Doesn't work though. The query just runs with the original date range. Even if my way worked, I still wouldn't like it though as it seems to be shot through with a precarious dependence (some sort of coupling?). Ie; if the date wheres aren't set up first then it all falls apart.
I could set the whole query up again from scratch, just with a different date range, but that seems ott as everything else in the query needs to be the same as the previous time it was used.
Any ideas for how to achieve this in the correct / neatest way are very welcome.
Thanks,
Geoff
You can use clone to duplicate the query and then run it with different where statements. First, build the query without the from-to constraints, then do something like this:
$query1 = $this->data_qry;
$query2 = clone $query1;
$result1 = $query1->where('from', $from1)->where('to', $to1)->get();
$result2 = $query2->where('from', $from2)->where('to', $to2)->get();
The suggestion from #lukasgeiter using clone is definitely the way to go; the reason is that an Eloquent\Builder object contains an internal reference to a Query\Builder that needs to be duplicated.
To keep the flow of your app and get back to a more functional style, you can use Laravel's with() helper, which simply returns the object passed in:
$result1 = with(clone $this->data_qry)->where('from', $from1)->where('to', $to1)->get();
$result2 = with(clone $this->data_qry)->where('from', $from2)->where('to', $to2)->get();
For the people who want a simpler and shorter syntax, you can daisy chain the clone() method on the query builder.
$result1 = $this->data_qry->clone()->where('from', $from1)->where('to', $to1)->get();
$result2 = $this->data_qry->clone()->where('from', $from2)->where('to', $to2)->get();

Categories