بهره وری دیتابیس پروژه لاراولی - استفاده از توابع aggregation

بهره وری دیتابیس پروژه لاراولی - استفاده از توابع aggregation

در مطلب قبلی با ایجاد رابطه های dynamic آشنا شدیم. در این قسمت خواهیم دید که چگونه می توان تعداد هایی را در دیتابیس با استفاده از شرط در کوئری به دست آورد. برای نشان دادن موضوع، پروژه زیر را ایجاد کردم که درخواست کاربر ها را پیگیری می کند. 

لینک فایل مقاله

هر درخواست کاربر یک عنوان، وضعیت و تعدادی نظر همراه خود دارد. تصور کنید یک نیازمندی جدید به وجود می آید که چند بخش به بالای صفحه اضافه کنید تا تعداد درخواست هایی که در هر مرحله قرار دارند را نشان دهید. برای راحتی کار بخش طراحی آن از قبل انجام شده و در فایل features.blade به صورت کامنت شده قرار دارد. به این فایل می رویم و کد ها را از حالت کامنت شده خارج می کنیم.

<div class="mb-12 grid grid-cols-3 gap-8">
    <div class="bg-orange-400 shadow rounded-lg flex items-center justify-between px-8 py-5">
        <div class="flex items-center">
            <svg class="text-white fill-current w-8 h-8" viewBox="0 0 20 20">
                <path d="M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" />
            </svg>
            <div class="ml-3 text-white font-medium">Requested</div>
        </div>
        <div class="text-white ml-3 text-2xl font-medium">{{ $statuses->requested }}</div>
    </div>
    <div class="bg-blue-500 shadow rounded-lg flex items-center justify-between px-8 py-5">
        <div class="flex items-center">
            <svg class="text-white fill-current w-7 h-7" viewBox="0 0 20 20">
                <path d="M6 2l2-2h4l2 2h4v2H2V2h4zM3 6h14l-1 14H4L3 6zm5 2v10h1V8H8zm3 0v10h1V8h-1z" />
            </svg>
            <div class="ml-3 text-white font-medium">Planned</div>
        </div>
        <div class="text-white ml-3 text-2xl font-medium">{{ $statuses->planned }}</div>
    </div>
    <div class="bg-green-400 shadow rounded-lg flex items-center justify-between px-8 py-5">
        <div class="flex items-center">
            <svg class="text-white fill-current w-8 h-8" viewBox="0 0 20 20">
                <path d="M0 11l2-2 5 5L18 3l2 2L7 18z" />
            </svg>
            <div class="ml-3 text-white font-medium">Completed</div>
        </div>
        <div class="text-white ml-3 text-2xl font-medium">{{ $statuses->completed }}</div>
    </div>
</div>

اکنون اگر مروگر را refresh کنیم، بخش جدید را می بینیم.

البته اگر به کنترلر FeatureController نگاه کنید، می بینید که تنها مقدار های فرضی به فایل blade داده شده است.

public function index()

{
    $statuses = (object)[];
    $statuses->requested = '-';
    $statuses->planned = '-';
    $statuses->completed = '-';

    $features = Feature::query()
        ->withCount('comments')
        ->paginate();

    return view('features', [
        'statuses' => $statuses,
        'features' => $features,
    ]);

}

پس چگونه به بهترین روش ممکن این تعداد ها را دریافت کنیم؟ یک راهکار این است که چند کوئری جداگانه به دیتابیس بزنیم که تعداد درخواست های هر وضعیت را حساب کند. این راه را امتحان می کنیم.

public function index()

{
    $statuses = (object)[];
    $statuses->requested = Feature::where('status','requested')->count();
    $statuses->planned = Feature::where('status','planned')->count();
    $statuses->completed = Feature::where('status','completed')->count();

    $features = Feature::query()
        ->withCount('comments')
        ->paginate();

    return view('features', [
        'statuses' => $statuses,
        'features' => $features,
    ]);

}

اگر مروگر را refresh کنیم، نتیجه دیده می شود.

اگر به سربرگ کوئری ها نگاه کنیم، سه کوئری ای که برای به دست آوردن تعداد ها زده ایم را می بینیم که در این شرایط مشکل خاصی نیست چون تنها سه وضعیت داریم. اما اگر 5 وضعیت داشتیم چه؟ یا 10 یا حتی بیشتر از آن. این همان وقتی است که کوئری های جدا مشکل ایجاد می کنند. می خواهم به شما تکنیکی که در این شرایط استفاده می کنم را نشان دهم.

ابتدا به سراغ یک ابزار مدیریت دیتابیس مانند table plus یا phpMyAdmin بروید تا کوئری را آن جا به دست آوریم. در اصل راهی وجود دارد که بتوانیم این تعداد ها را با یک کوئری به دست آوریم. احتمالا با نوشتن کوئری ای مانند زیر آشنایی دارید.

SELECT COUNT(*) from features

اگر این کوئری را اجرا کنید نتیجه آن 60 خواهد بود.

 چیزی که احتمالا نمی دانید این است که می توان دستورهای شرطی را داخل توابعی مانند COUNT و SUM و... نوشت. به کوئری بالا شرط اضافه می کنیم. 

SELECT COUNT(CASE WHEN status = 'Requested' THEN 1 END) from features

اگر کوئری بالا را اجرا کنیم نتیجه آن 47 خواهد بود. اگر به صفحه  اصلی برنامه نگاه کنیم می بینیم که این مقدار درست است. حال یک شرط دیگر اضافه می کنیم.

SELECT 
COUNT(CASE WHEN status = 'Requested' THEN 1 END),
COUNT(CASE WHEN status = 'Planned' THEN 1 END)
from features

اگر این کوئری را اجرا کنیم دو تعداد در نتیجه می گیریم 47 و 3 که هر دو درست اند. این کار را برای وضعیت سوم هم انجام می دهیم.

SELECT 
COUNT(CASE WHEN status = 'Requested' THEN 1 END),
COUNT(CASE WHEN status = 'Planned' THEN 1 END),
COUNT(CASE WHEN status = 'Completed' THEN 1 END)
from features

با اجرای کوئری بالا این نتیجه را دریافت می کنیم.

جمع این سه عدد 60 می شود که تعداد کل درخواست ها بود. البته در حال حاضر اسم ستون هایی که در نتیجه آمده اند خوب نیست. به همین خاطر به هر کدام از آن ها یک نام مستعار می دهیم.

SELECT 
COUNT(CASE WHEN status = 'Requested' THEN 1 END) as requested,
COUNT(CASE WHEN status = 'Planned' THEN 1 END) as planned,
COUNT(CASE WHEN status = 'Completed' THEN 1 END) as completed
from features

اکنون نتیجه ای مانند زیر دریافت می شود.

حال به کنترلر FeatureController می رویم تا کوئری را در آنجا بنویسیم.

public function index()

{
    $statuses = Feature::toBase()
        ->selectRaw("count(case when status = 'Requested' then 1 end) as requested")
        ->selectRaw("count(case when status = 'Planned' then 1 end) as planned")
        ->selectRaw("count(case when status = 'Completed' then 1 end) as completed")
        ->first();

    $features = Feature::query()
        ->withCount('comments')
        ->paginate();

    return view('features', [
        'statuses' => $statuses,
        'features' => $features,
    ]);

}

به این دلیل که نمی خواهیم نتیجه دریافتی یک شی از مدل Feature باشد متد toBase را صدا زدیم (با اجرای این متد مدل به دست آمده به شی ای از  query builder تبدیل می شود). سپس از متد selectRaw برای نوشتن کوئری استفاده کردیم و در نهایت از آن جایی که می دانیم این کوئری تنها یک row بر می گرداند متد first را به جای get صدا زدیم.

اکنون می توانیم به مروگر برگشته و صفحه را refresh کنیم.

همان طور که می بینید همه چیز به درستی کار می کند و تعداد کوئری های زده شده 3 است.

امیداروم این مطلب برای شما مفید باشد.

از بهترین نوشته‌های کاربران سکان آکادمی در سکان پلاس


online-support-icon