در بخش های قبل به جدول های companies و users ایندکس هایی را اضافه کردیم تا سرعت کوئری جست و جو برنامه را افزایش بدهیم. اما متوجه شدیم که کوئری ما نمی تواند از ایندکس ها استفاده کند. برای حل این مشکل عبارت %
را از قبل کلمه ی جست و جو شده حذف کردیم. اما هنوز یک مشکل حل نشده باقی مانده و آن sub query مربوط به جدول companies است.
در این بخش گزینه هایی را بررسی می کنیم که کوئری بهتری از orWhereHas اجرا می کنند.
هنگامی که یک sub query مشکل ایجاد می کند، اولین جایگرین استفاده از join است. در متد search ای که نوشتیم به جدول comapnies، join می زنیم و orWhereHas را با orWhere عادی جایگزین می کنیم.
public function scopeSearch($query, string $terms = null)
{
$query->join('companies', 'companies.id', '=', 'users.company_id');
collect(str_getcsv($terms, ' ', '"'))->filter()->each(function ($term) use ($query) {
$term = $term . '%';
$query->where(function ($query) use ($term) {
$query->where('first_name', 'like', $term)
->orWhere('last_name', 'like', $term)
->orWhere('companies.name', 'like', $term);
});
});
}
زمان کوئری ما پیش از این تغییر حدود 400 میلی ثانیه بود. اکنون به صفحه کاربر ها می رویم تا تاثیر این تغییر در زمان کوئری را ببینیم.
مجموع زمان کوئری ها کمی کمتر شده است. اما من همچنان فکر می کنم که از Index جدول companies استفاده نمی شود. کوئری زده شده را کپی می کنیم تا آن را در phpMyAdmin یا هر برنامه مدیریت دیتابیس دیگری به همراه عبارت explain اجرا کنیم.
می توان دید که index مورد نظر ما در ستون possible keys وجود دارد اما هنوز استفاده نمی شود. تا زمانی که موفق نشویم از Index ها بهره ببریم، زمان کوئری به حد مطلوب نمی رسد.
یکی از مشکل هایی که join و sub query دارند این است که جدول users را به صورت مستقیم به companies وصل می کنند. در هر دو روش ستون id جدول companies به ستون company_id جدول users وصل می شود. به همین دلیل جدول companies به جدول users وابسته می شود. یکی از راه های حل این مشکل، استفاده از whereIn است. whereIn برای حذف وابستگی بین دو جدول به ما کمک می کند.
به متد search می رویم و این بار از whereIn استفاده می کنیم.
public function scopeSearch($query, string $terms = null)
{
collect(str_getcsv($terms, ' ', '"'))->filter()->each(function ($term) use ($query) {
$term = $term . '%';
$query->where(function ($query) use ($term) {
$query->where('first_name', 'like', $term)
->orWhere('last_name', 'like', $term)
->orWhereIn('company_id', function ($query) use ($term) {
$query->select('id')
->from('companies')
->where('name', 'like', $term);
});
});
});
}
ابتدا join را حذف کردیم. سپس با استفاده از sub-query مقدار id شرکت هایی که نام آن ها با عبارت جست و جو شده تطبیق داشت را بدست آوردیم و از این sub-query در ورودی دوم orWhereIn استفاده کردیم.
اکنون به صفحه کاربر ها می رویم تا تاثیر این تغییر را در زمان کوئری ها ببینیم.
همانطور که می بینید زمان اجرای کوئری ها حدود 200 میلی ثانیه کمتر شده است. این یک بهبود بسیار بزرگ محسوب می شود.
اکنون explain کوئری جدید را مشاهده می کنیم تا ببینیم از index استفاده شده است یا خیر.
می توانیم ببینم که اکنون کوئری ما از Index جدول companies استفاده می کند و به همین دلیل است که زمان کوئری بهبود یافته. بهتر است همیشه سعی کنید بین کوئری ها وابستگی وجود نداشته باشد. در این مثال استفاده از whereIn همان نتیجه ی join و whereHas را داشت اما با سرعت بسیار بهتر. همانطور که در تصویر می بینید، کوئری ما هنوز نمی تواند از Index جدول users استفاده کند. در قسمت بعدی سعی می کنیم این مشکل را برطرف کنیم.