ردیف ها (rows) در پایگاه داده PDO

16 بهمن 1397
درسنامه درس 9 از سری آموزش PDO
PDO-rows

با سلام، در این قسمت از سری آموزشی ارتباط شیء گرا با پایگاه داده (PDO) به سراغ مباحث پیرامون ردیف ها (مانند تعداد ردیف ها و ردیف های تاثیر گرفته) می پردازیم.

تعداد ردیف ها در PDO

رابط PDO تابعی به نام ()PDOstatement::rowCount ارائه می دهد که تعداد ردیف های پیدا شده توسط کوئری را به شما برمیگرداند. این تابع بسیار به ندرت استفاده می شود و اگر بخواهم صادقانه تر بگویم احتمالا هیچ وقت در برنامه های عادی خود از آن استفاده نکنید اما از باب آشنایی توضیح کوتاهی از آن ارائه می دهیم.

اگر از قبل با این تابع آشنا بوده اید میدانید که این تابع یکی از آن توابعی است که در موارد بسیار زیادی به شکل اشتباه استفاده شده است.

توسعه دهندگان مبتدی و حتی بعضا حرفه ای بیشتر اوقات برای شمردن چیزی از آن استفاده نمی کنند بلکه با آن چک می کنند تا ببینند داده ای برگشت داده شده است یا نه. این کار اشتباهی است! چرا که برای چنین مواقعی خود داده را دارید! یعنی از دستورات ()fetch و ()fetchAll استفاده کنید و دقیقا همین کار را برایتان انجام می دهند.

فرض کنید می خواهیم ببینیم آیا کاربری با فلان نام خاص وجود دارد یا خیر. اگر وجود داشت تنها یک ردیف را انتخاب می کنیم:

$stmt = $pdo->prepare("SELECT 1 FROM users WHERE name=?");
$stmt->execute([$name]);
$userExists = $stmt->fetchColumn();

این حالت دقیقا برای دریافت یک ردیف یا چندین ردیف در یک آرایه یکی است:

$data = $pdo->query("SELECT * FROM table")->fetchAll();
if ($data) {
    // در اینجا داده را دارید و نیازی به تابع دیگری نیست
}

اگر به کد بالا توجه کنید می بینید که نیازی به تابع ()rowCount نیست چرا که اینجا نیازی به تعداد دقیق ردیف ها نداریم بلکه نیاز به یک flag بولین داریم که بگوید آیا داده ای داریم یا خیر. به همین سادگی و با کد بالا می توانید این کار را انجام دهید و حافظه و منابع سرور را هدر ندهید.

استفاده ی اشتباه دیگری که از تابع ()rowCount می شود این است که برخی برنامه نویسان مبتدی از آن برای شمردن ردیف های پایگاه داده استفاده می کنند!!

هیچ گاه از چنین روشی استفاده نکنید چرا که تنها نتیجه ی آن هدر دادن منابع سرور است.

برای چنین کاری شما باید از پایگاه داده بخواهید ردیف ها را برای شما بشمارد و مقدار را در یک ردیف تحویل بدهد:

$count = $pdo->query("SELECT count(1) FROM t")->fetchColumn();

این تنها راه صحیح برای چنین موقعیتی است.

به طور خلاصه:

  • اگر می خواهید بدانید در یک جدول چند ردیف موجود است از دستور (*)SELECT COUNT استفاده کنید.
  • اگر می خواهید بدانید آیا کوئری شما داده ای را برگردانده یا خیر، خود داده را چک کنید.
  • اگر می خواهید بدانید کوئری شما چند ردیف را برگردانده (گرچه چنین حالتی تقریبا هیچ گاه پیش نمی آید) از دستور ()rowCount استفاده کرده و یا تابع ()count را روی آرایه ی برگشت داده شده از ()fetchAll اجرا کنید.

بر اساس چیزی که گفته شد می فهمیم این جواب در stackoverflow نامناسب و بی معنی است. دستور ()rowCount هیچ گاه جایگزین کوئری (*)SELECT COUNT نمی شود چرا که هدف این دو کاملا متفاوت است.

صدا زدن یک کوئری برای دریافت تعداد ردیف های برگشتی از یک کوئری دیگر کاری بسیار عجیب و صد البته اشتباه است. به این ترتیب کوئری های خود را دو برابر کرده ایم!

ردیف های تاثیر گرفته

PDO برای دریافت تعداد ردیف های تاثیر گرفته1 از دستور SELECT و همچنین تعداد ردیف های تاثیر گرفته از کوئری های DML (کوئری های INSERT و UPDATE و DELETE) از یک تابع استفاده میکند.

()PDOstatement::rowCount بنابراین برای دریافت تعداد ردیف های تاثیر گرفته، پس از اجرای کوئری این تابع را صدا بزنید.

1- منظور ما از ردیف های تاثیر گرفته (affected) ردیف هایی است که به خاطر استفاده از یک دستور یا کوئری خاص تحت تاثیر قرار گرفته اند. این تاثیر می تواند به شکل های مختلفی (مثل آپدیت شدن و تغییر کردن و ...) باشد.

مسئله ی دیگر این است که اگر مقدار جدید با مقدار قبلی در جدول یکی باشد، mysql دیگر آن ردیف را آپدیت نمی کند؛ بنابراین ممکن است تعداد ردیف های تاثیر گرفته با تعداد ردیف هایی که با کوئری WHERE منطبق هستند (match می شوند) فرق داشته باشد. گرچه می توان به دستور ()rowCount اعلام کرد که به جای تعداد ردیف های تاثیر گرفته، تعداد ردیف هایی که با کوئری منطبق هستند را برگرداند (با استفاده از PDO::MYSQL_ATTR_FOUND_ROWS) اما متاسفانه این دستور connection-only (مخصوص اتصال) است؛ یعنی تنها هنگام ساخت کانکشن تعیین می شود و در هنگام اجرای دستورات مختلف نیز تغییر نمی کند.

بنابراین باید یک حالت را از ابتدا انتخاب کرده و تا انتها با همان پیش بروید.

نکته ی مهم: متاسفانه دستور PDO::MYSQL_ATTR_FOUND_ROWS همیشه کار نمی کند و مسئله ی عجیب تر این است که اگر کار نکند، به شما هیچ اخطاری نیز نمی دهد! تنها راهی که می توانید از طریق آن بفهمید این دستور کار می کند یا خیر استفاده از (pdo->getAttribute(PDO::MYSQL_ATTR_FOUND_ROWS$ است.

متاسفانه در PDO تابعی معادل با mysql(i)_info وجود ندارد که یکی از محدود معایب آن است.

همچنین برای دریافت ID مربوط به آخرین ردیف INSERT شده می توانید از دستور PDO::lastInsertId استفاده کنید.

خلاصه ی مقاله

در این مقاله با مباحث پیرامون ردیف ها (مانند تعداد ردیف های تاثیر گرفته، تعداد کل ردیف های برگردانده شده و...) آشنا شدیم. امیدوارم از این قسمت لذت برده باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش PDO توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
16 بهمن 1397
دوستان بهتره همیشه آپدیت های جدید را در صفحه ی رسمی PHP نیز چک کنید. بسیار کمک خواهد کرد مخصوصا از این نظر که مثال های زیادی از طرف مردم ارسال میشه.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.