I am using Laravel spatie package for query builder and when I run my query it shows in the telescope that it runs twice the only difference between them is this line:
select
count(*) as aggregate
from
`accommodations`
where
.
.
.rest of my query
and the other one is like this :
select
*
from
`accommodations`
where
.
.
.
limit
10 offset 0
any idea what is wrong here because every one of those queries takes about 5 sec and that would be about 10 to 12 second extra for me. thanks
EDIT
$data = QueryBuilder::for(Accommodation::class)
->allowedFilters([
AllowedFilter::scope('bed_count'),
AllowedFilter::scope('filter_price'),
AllowedFilter::scope('filter_date'),
AllowedFilter::scope('discounts'),
AllowedFilter::exact('grade_stars'),
AllowedFilter::exact('city_id'),
AllowedFilter::exact('is_recommended'),
AllowedFilter::exact('accommodation_type_id'),
'name',
])
->allowedAppends(['cheapestroom'])
->allowedIncludes(['gallery','city','accommodationRooms','accommodationRooms.roomPricingHistorySearch','discounts','prices'])
->allowedSorts([
AllowedSort::custom('discount', new DiscountSort() ,'amount'),
AllowedSort::custom('price', new PriceSort() ,'price'),
])
->paginate(10);
this is my spatial query builder and this is the part that takes 5 secs twice :
class DiscountSort implements Sort
{
public function __invoke(Builder $query, bool $descending, string $property) : Builder
{
$direction = $descending ? 'DESC' : 'ASC';
$data = $query->join('accommodation_rooms', 'accommodations.id', '=', 'accommodation_rooms.accommodation_id')
->join('discounts', 'accommodation_rooms.id', '=', 'discounts.accommodation_room_id')
->select('accommodation_rooms.id')
->orderBy('discounts.amount', 'desc')
->select('discounts.amount', 'accommodations.*')
->groupBy('discounts.amount', 'accommodation_rooms.id')
;
return $data;
}
Its a join and because laravel and this package are not able to order data by relationship or nested relationship value I should have used a join.
Can that second query be the cause of pagination ??
It's in the paginate, the pagination needs to know how many records are in the full result, to display all the needed informations; therefor is the count query.
The second one gets the results for the current page of your pagination ;)
Related
I'm using laravel clockwork to monitor my queries
in my controller I have
public function index(){
$errorFound = false;
$error = ['error' => 'No Monitor Found'];
$urls = $this->url->with('url_status','latestUrlStatus','users');
if (request()->has('q')) {
$keyword = '%'.request()->get('q').'%';
$builder = $urls->where('description', 'like', $keyword);
$builder->count() ? $urls = $builder : $errorFound = true;
}
return $errorFound === false ? UrlsResource::collection($urls->latest()->paginate(5)->appends(request()->query())) : $error;
}
on my laravel clockwork im getting
doubled queries
is it normal? if it is a problem how can I fix this? TIA
There's no problem. All of those queries are expected.
The first query (select users...) isn't from the code you've shown. It came from TrustProxies.
The second query (select count()) is from $builder->count().
All the rest of the queries come from $urls->latest()->paginate(5). The first thing paginate() does is run a count() query (the third query) to get the total number of records. Then it moves on to call the real queries.
In this case, the fourth query is your main query for all your urls, the fifth query is the query to eager load your url_status relationship, the sixth query is the query to eager load your latestUrlStatus relationship, and the seventh query is the query to eager load your users relationship.
I have 5 database rows with the same client_id, 3 labelled completed, Yes.
This code pulls through 3 results as expected:
$indGoal = $client->indGoal()->where('completed','=','Yes')->get();
This code pulls through no results: I would expect 2.
$indGoal = $client->indGoal()->where('completed','!=','Yes')->get();
This question suggests adding ->orWhereNull('completed') - which works, but ignores the client_id relationship. The request brings through all non-Yes results, regardless of $client
My Client model for reference:
public function indGoal()
{
return $this->hasMany('App\Models\IndGoal');
}
You should group orWhere filters in a callback so they don't interfere with existing filters.
$indGoal = $client->indGoal()
->where(function ($query) {
$query->orWhere('completed', '!=', 'yes')
->orWhereNull('completed');
})
->get();
This way, the query builder knows any of the grouped conditions should be true and all other conditions are independent.
I have this code written in using the laravel and I wonder how to write it in pure SQL without using laravel, as would be?
Route::get('popular', function(){
$media = Media::where('active', '=', '1')->join('media_likes', 'media.id', '=', 'media_likes.media_id')->groupBy('media_likes.media_id')->orderBy(DB::raw('COUNT(media_likes.id)'), 'DESC')->select('media.*')->paginate(30);
$data = array(
'media' => $media,
'categories' => Category::all(),
'pages' => Page::all(),
'settings' => Setting::first(),
);
return View::make('Theme::home', $data);
});
Using toSql method
Laravel Query builder has a helpful method called toSql which is handy to see what the final SQL query looks like.
Now, when you call the paginate method, you'll get a Paginator and you won't be able to call the toSql method.
Deconstructing paginate
When you use paginate, Laravel will make the following query for each $page:
// Let's simplify the query for now
Media::skip(($page - 1) * $perPage)->take($perPage);
Knowing that, you can use the query builder's method toSql and you'll see the actual sql string.
Supossing $page=3, $perPage=30, and that the name of the table is media, you'll get something like this:
Media::skip(2 * 30)->take(30)->toSql();
select * from media limit 30 offset 60
Now, for your actual query, you can use the following to see the resulting SQL string for the page 3 (as an example):
Media::where('active', '=', '1')
->join('media_likes', 'media.id', '=', 'media_likes.media_id')
->groupBy('media_likes.media_id')
->orderBy(DB::raw('COUNT(media_likes.id)'), 'DESC')
->select('media.*')
->skip(2 * 30)
->take(30)
->toSql();
Listening For Query Events
Alternatively, you can set an event listener in your AppServiceProvider, and log each SQL query the application executes.
public function boot()
{
\DB::listen(function ($query) {
\Log::debug($query->sql);
\Log::debug($query->bindings);
\Log::debug($query->time);
});
}
$media = Media::where('active', '=', '1')->join('media_likes', 'media.id', '=', 'media_likes.media_id')->groupBy('media_likes.media_id')->orderBy(DB::raw('COUNT(media_likes.id)'), 'DESC')->select('media.*')->paginate(30);
Something like this:
SELECT m.*
FROM
media m
JOIN
media_likes ml
ON
ml.media_id = m.id
GROUP BY
ml.media_id
ORDER BY
COUNT(ml.id) DESC
what i am trying to do is to get distinct values (date) of my Model and for each date the display the corresponding data. When i make pagination of the distinct dates, it works fine, but in the pagination pages i see all the results instead of the distinct. Here is my function:
public function showdaily($id) {
$capacities = array();
// DB::enableQueryLog();
$capacity_daily = CapacityDaily::select('for_date')->where('capacity_id', '=', $id)->orderBy('for_date', 'asc')->distinct()->paginate(2);
// dd(DB::getQueryLog());
foreach($capacity_daily as $cap) {
$get_capacity = CapacityDaily::where('capacity_id', '=', $id)->where('for_date', '=', $cap->for_date)->orderBy('for_date', 'asc')->distinct()->get();
$capacities[] = array('for_date' => $cap->for_date, 'values' => $get_capacity, 'capacity' => Capacity::find($id)->first());
}
return view('admin.capacity.daily', compact('capacities', 'capacity_daily', 'id'));
}
I have totally 4 distinct dates and 96 rows in that table. As i want to display only 2 dates per page it should show me only one additional page available, but instead of that i have from 1-47.
What i make wrong ?
The toSql() method will show the query it is running, but I suspect you need to group on for_date rather than use distinct
$capacity_daily = CapacityDaily::select('for_date')->where('capacity_id', '=', $id)->orderBy('for_date', 'asc')->groupBy('for_date')->paginate(2);
So using Laravel 4.2, I'm attempting to run a query on a very large database of 600,000 records. When doing so, I get the following error:
SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. (SQL: select 'username', 'timestamp', 'ip', 'appID' from 'UserLog' where 'id' > 1179525)
Here is my code:
public static function getUserLogData()
{
$getUsers = DB::connection('PORTAL2')->table('UserLog')
->select('username', 'timestamp', 'ip', 'appID')
->where('id', '>', '1179525')
->get();
$numOfRowsReturned = count($getUsers);
if($numOfRowsReturned>0)
{
return $getUsers;
}
return 0;
}
To explain the magic number 1179525 , I'm using the ID to attempt to select only 10 rows, as there are only 10 id's after 1179525.
The query works just fine when the code looks like this (although I'd like to select ~500 rows, not 1):
public static function getUserLogData()
{
$getUsers = DB::connection('PORTAL2')->table('UserLog')
->select('username', 'timestamp', 'ip', 'appID')
->where('id', '>', '1179525')
->first();
$numOfRowsReturned = count($getUsers);
if($numOfRowsReturned>0)
{
return $getUsers;
}
return 0;
}
Any ideas as to how to solve this issue?
Instead of ->get() use ->take(10) or any number to specify the limit on the number of rows returned. ->first() is really ->take(1).
Also instead of doing count($getUsers) and then checking if it is > 0, use the
`if (!empty($getUsers))`
it will be more efficient with large data sets and does what you mean to do. count() will traverse the array, empty() will only check if it is not null or 0 length.