PDA

View Full Version : یک مسئله در Pagination



eshpilen
شنبه 29 بهمن 1390, 19:07 عصر
فرض کنید ما رکورد 1 تا 10 رو توی یک صفحه نشون میدیم.
فکر میکنم با کوئری ای مثل کوئری زیر این کار رو انجام میدیم:

select * from `table1` limit 10
بعد که کاربر روی لینک صفحهء شماره 2 کلیک میکنه تا 10 رکورد بعدی رو ببینه، از این کوئری استفاده میکنیم:

select * from `table1` limit 10 offset 10

درسته؟
روش استاندارد اینه دیگه؟

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

آیا این یک مشکل نیست؟
و اگر هست، چه روشی رو برای حلش پیشنهاد میکنید.

MMSHFE
شنبه 29 بهمن 1390, 19:26 عصر
دو راه حل وجود داره:
1- از حذف منطقی استفاده کنید.
2- id آخرین رکورد رو بدست بیارین و با کمک where رکوردهای بعد از اون رو انتخاب کنید. مثلاً اگه رکوردهای 1 تا 10 رو با دستورات زیر نشون میدین:


$result = mysql_query('SELECT * FROM `table` ORDER BY `id` LIMIT 0, 10');
while($row = mysql_fetch_assoc($result) {
// process your data here
$last_id = $row['id'];
}

به خط آخر حلقه while دقت کنید.
برای رکوردهای 11 تا 20 از این کد استفاده کنید:


$result = mysql_query('SELECT * FROM `table` WHERE (`id`>\''.$last_id.'\') ORDER BY `id` LIMIT 0, 10');
while($row = mysql_fetch_assoc($result) {
// process your data here
$last_id = $row['id'];
}

البته روش حذف منطقی مناسبتر هست.
البته یک راه سوم هم وجود داره:
برای کاربر یک جدول موقت در MEMORY (انجین Memory) ایجاد کنید و اطلاعات رو از اون واکشی کنید. اصلاً میشه کل رکوردهای استخراج شده رو توی سشن بگذارین!
موفق باشید.

masoud_tamizy
شنبه 29 بهمن 1390, 20:04 عصر
البته انقدرها هم مهم نیست این مساله که شما می فرمایید ولی اگه خیلی اصرار دارید راه حل آقای شهرکی راه حل خوبیه :لبخندساده:

lady64
یک شنبه 30 بهمن 1390, 08:09 صبح
من هم با paging یه مشکل دارم . اینکه اول لینک صفحات رو نشون میده و باید حتما با کلیک بر روی لینک صفحه ی اول ، صفحه ی اول رو نشون بده که قشنگ نیست.حالا میخوام از این کد که فکر کنم از این تالار گرفتم استفاده کنم ، اما در خط 38 :

$query = "SELECT COUNT(code) AS numrows FROM personel";

AS numrows برای چیه ؟ متوجه نشدم.
کدی که از تالار گرفتم :
82904

eshpilen
یک شنبه 30 بهمن 1390, 08:41 صبح
البته انقدرها هم مهم نیست این مساله که شما می فرمایید ولی اگه خیلی اصرار دارید راه حل آقای شهرکی راه حل خوبیه :لبخندساده:
هرچیزی در جای خاصی ممکنه مهم بشه.
بستگی به کاربرد داره.

masoud_tamizy
یک شنبه 30 بهمن 1390, 09:09 صبح
من هم با paging یه مشکل دارم . اینکه اول لینک صفحات رو نشون میده و باید حتما با کلیک بر روی لینک صفحه ی اول ، صفحه ی اول رو نشون بده که قشنگ نیست.حالا میخوام از این کد که فکر کنم از این تالار گرفتم استفاده کنم ، اما در خط 38 :

$query = "SELECT COUNT(code) AS numrows FROM personel";

AS numrows برای چیه ؟ متوجه نشدم.
کدی که از تالار گرفتم :
82904
فقط یه اسمه برای دستیابی به مقدار COUNT(code)

eshpilen
یک شنبه 30 بهمن 1390, 09:44 صبح
دو راه حل وجود داره:
1- از حذف منطقی استفاده کنید.
البته روش حذف منطقی مناسبتر هست.

منظورت اینه که بجای حذف فیزیکی یک فیلد Boolean بذارم که نشون میده رکورد حذف شده یا نه؟
اگر تعداد و حجم رکوردهای حذف شده زیاد بشه چی؟


برای کاربر یک جدول موقت در MEMORY (انجین Memory) ایجاد کنید و اطلاعات رو از اون واکشی کنید. اصلاً میشه کل رکوردهای استخراج شده رو توی سشن بگذارین!
ممکنه حجم داده ها زیاد باشه. البته اینکه در سشن ذخیره کنیم به فکر بنده هم رسیده بود.
راستی این جدول موقت در MEMORY قاعدتا بین درخواستها در RAM باقی میمونه. پس باید یه مکانیزم مطمئنی برای حذفش باشه. چه مکانیزمی رو پیشنهاد میکنید؟

lady64
یک شنبه 30 بهمن 1390, 10:28 صبح
فقط یه اسمه برای دستیابی به مقدار COUNT(code)

یعنی تعداد فیلد بدست اومده از
count(code)

هر چی که باشه در متغیر numrows ریخته میشه ؟ عجیبه این کد رو تا حالا ندیده بودم !!!

باز متوجه نشدم ، لطفا بیشتر توضیح بدید.....

MMSHFE
یک شنبه 30 بهمن 1390, 10:42 صبح
ببینید، وقتی میگیم:
SELECT COUNT(`code`) AS `numrows` FROM `table`
یعنی اینکه تعداد همه رکوردها جدول table رو برام برگردون. همونطور که میدونید خروجی دستور SELECT همیشه یک جدول هست. بنابراین، باید فیلدهای اون دارای اسم باشن. اگه به فیلدهایی که ازطریق محاسبه استخراج میشه (مثل همین COUNT) صراحتاً اسم ندیم، بسته به نوع طراحی DBMS ممکنه اسامی مختلفی بطور خودکار بهشون داده بشه. مثلاً بعضیها این فیلدها رو با اسامی Expr1 و Expr2 و... شماره میگذارن و بعضیها هم اینطوری: count of code و...
حتی ممکنه از یک نسخه به نسخه بعدی DBMS سیاستهای نامگذاری اونها تغییر کنه. بنابراین، با استفاده از کلمه کلیدی AS یک اسم برای اون فیلد انتخاب میکنیم. البته AS مختص فیلدها نیست و میشه باهاش برای جداول هم اسم مستعار بگذاریم. برای مثال، کد زیر:
SELECT `t1`.`name`,`t2`.`grade` WHERE (`t1`.`name`=`t2`.`name`) FROM `names_of_students` AS `t1`, `grades_of_students` AS `t2`
توی کد فوق، میبینید که اسم مستعار چقدر کمک کرده که از تایپ اسامی طولانی در کد پرهیز کنیم. موفق باشید.

MMSHFE
یک شنبه 30 بهمن 1390, 10:48 صبح
منظورت اینه که بجای حذف فیزیکی یک فیلد Boolean بذارم که نشون میده رکورد حذف شده یا نه؟
اگر تعداد و حجم رکوردهای حذف شده زیاد بشه چی؟

روشی که گفتم، ساده ترین روشه اما بهترین روش نیست. طبیعتاً در حجم بالای داده ها مشکل افت سرعت رو خواهیم داشت. البته معمولاً تا چندین هزار رکورد مشکلی پیش نمیاره.

ممکنه حجم داده ها زیاد باشه. البته اینکه در سشن ذخیره کنیم به فکر بنده هم رسیده بود.
راستی این جدول موقت در MEMORY قاعدتا بین درخواستها در RAM باقی میمونه. پس باید یه مکانیزم مطمئنی برای حذفش باشه. چه مکانیزمی رو پیشنهاد میکنید؟
طبیعتاً از اونجا که جداول با انجین MEMORY توی حافظه HEAP تولید میشن و این حافظه برخلاف STACK بطور خودکار آزاد نمیشه، باید حواسمون به حذف جداول بعد از اتمام کار باشه. این مثال ساده رو ببینید:


mysql> CREATE TABLE test ENGINE=MEMORY
-> SELECT ip,SUM(downloads) AS down
-> FROM log_table GROUP BY ip;
mysql> SELECT COUNT(ip),AVG(down) FROM test;
mysql> DROP TABLE test;

منبع (http://dev.mysql.com/doc/refman/5.0/en/memory-storage-engine.html)
موفق باشید.

eshpilen
یک شنبه 30 بهمن 1390, 12:26 عصر
طبیعتاً از اونجا که جداول با انجین MEMORY توی حافظه HEAP تولید میشن و این حافظه برخلاف STACK بطور خودکار آزاد نمیشه، باید حواسمون به حذف جداول بعد از اتمام کار باشه. این مثال ساده رو ببینید:


mysql> CREATE TABLE test ENGINE=MEMORY
-> SELECT ip,SUM(downloads) AS down
-> FROM log_table GROUP BY ip;
mysql> SELECT COUNT(ip),AVG(down) FROM test;
mysql> DROP TABLE test;

منبع (http://dev.mysql.com/doc/refman/5.0/en/memory-storage-engine.html)
موفق باشید.
اینکه نشد.
اصلا چه کاریه. جدول log_table رو یک بار خونده ریخته توی جدول دیگه از نوع مموری و بعد دوباره از مموری خونده و بعد جدول مموری رو دلیت کرده!! این کارها اصلا برای چی؟ خب اگر میخواستیم فقط یک کوئری بزنیم که از همون ابتدا از جدول اصلی میخوندیم.
مشکل اینه که میخوایم جدول موقتی در مموری باقی بمونه و در چند درخواست ازش استفاده کنیم. ولی اینکه آخرین درخواست مورد نیاز کدام باشه مشخص نیست و ما نمیدونیم چه وقت باید جدول رو دلیت کنیم.

mtchabok
یک شنبه 30 بهمن 1390, 12:57 عصر
اگه می خواین که فقط جدول رو برای یک درخواست کلاینت نگه دارید می تونید از یه کلاس استفاده کنین و زمانیکه شئی کلاس در حال از بین رفتن هس به دیتابیس دستور حذف جدول رو بده .
ولی در صورتی که میخواید این جدول برای چندنین درخواست کلاینت باقی بمونه ، اینجاس که باید به صورت هوشمند عمل بشه و یه مکانیزمی برای حذف جدول استفاده کرد .

MMSHFE
یک شنبه 30 بهمن 1390, 16:04 عصر
اینکه نشد.
اصلا چه کاریه. جدول log_table رو یک بار خونده ریخته توی جدول دیگه از نوع مموری و بعد دوباره از مموری خونده و بعد جدول مموری رو دلیت کرده!! این کارها اصلا برای چی؟ خب اگر میخواستیم فقط یک کوئری بزنیم که از همون ابتدا از جدول اصلی میخوندیم.
مشکل اینه که میخوایم جدول موقتی در مموری باقی بمونه و در چند درخواست ازش استفاده کنیم. ولی اینکه آخرین درخواست مورد نیاز کدام باشه مشخص نیست و ما نمیدونیم چه وقت باید جدول رو دلیت کنیم.
عزیز دل، این فقط یک مثاله. فرض کنید میخواین چندین کار پیچیده رو روی دیتابیس انجام بدین. طبیعتاً اجرای کوئریها روی RAM خیلی سریعتر از دیسک هست. بنابراین اطلاعات رو روی RAM میاریم و پردازشها رو انجام میدیم و بعد ازبین میبریم. حالا میشه کاری کرد که برای درخواستهای طولانی تر اطلاعات حفظ بشه. مثلاً برای کاربران بیایم و توی حافظه جدولی با سشن آیدی اونها ایجاد کنیم و هرموقع سشن از بین رفت، جدول رو هم Drop کنیم. البته باید حواسمون باشه که از انجین Memory برای کارهای اصلی و نگهداری اطلاعات برای مدت طولانی استفاده نکنیم چون با Restartشدن موتور MySQL و یا سرور، این اطلاعات حذف میشن. موفق باشید.

narsic
یک شنبه 30 بهمن 1390, 16:23 عصر
فرض کنید ما رکورد 1 تا 10 رو توی یک صفحه نشون میدیم.
فکر میکنم با کوئری ای مثل کوئری زیر این کار رو انجام میدیم:

select * from `table1` limit 10
بعد که کاربر روی لینک صفحهء شماره 2 کلیک میکنه تا 10 رکورد بعدی رو ببینه، از این کوئری استفاده میکنیم:

select * from `table1` limit 10 offset 10

درسته؟
روش استاندارد اینه دیگه؟

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

آیا این یک مشکل نیست؟
و اگر هست، چه روشی رو برای حلش پیشنهاد میکنید.
چرا این موضوع براتون اهمیت داره؟
در انتخاب دوم در صورتی که رکوردهای ۱۲و ۱۳ حذف شده باشن Mysql چون از دهمین رکورد قصد خوندن ۱۰ رکورد رو داره میره و رکوردهای ۲۱و ۲۲ رو بهم به نتیجه اضافه میکنه.
حالا مشکل شما این اضافه شدن ID هاست؟ یا اینکه نه فرض میکنید با اجرای دستور دوم MYSQL از رکوردهایی با ID ۱۰ تا ۲۰ رو بیرون میکشه و در حالت حذف رکوردهای حذف شده جای خالی پیدا میکنن؟

eshpilen
یک شنبه 30 بهمن 1390, 18:09 عصر
چرا این موضوع براتون اهمیت داره؟
در انتخاب دوم در صورتی که رکوردهای ۱۲و ۱۳ حذف شده باشن Mysql چون از دهمین رکورد قصد خوندن ۱۰ رکورد رو داره میره و رکوردهای ۲۱و ۲۲ رو بهم به نتیجه اضافه میکنه.
حالا مشکل شما این اضافه شدن ID هاست؟ یا اینکه نه فرض میکنید با اجرای دستور دوم MYSQL از رکوردهایی با ID ۱۰ تا ۲۰ رو بیرون میکشه و در حالت حذف رکوردهای حذف شده جای خالی پیدا میکنن؟
د نگرفتی قضیه رو!
گفتم رکورد 3 و 4 نه 12 و 13.

narsic
یک شنبه 30 بهمن 1390, 22:46 عصر
چه فرقی میکنه!!؟ من فقط قصدم مثال زدن بود کل سوآل چی؟

MMSHFE
دوشنبه 01 اسفند 1390, 00:10 صبح
منظورشون اینه که اگه اول همه رکوردها درست باشه، توی صفحه 1 رکوردهای 1 تا 10 رو میبینیم و اگه قبل از اینکه به صفحه 2 بریم، رکوردهای 3 و 4 حذف بشن، با رفتن به صفحه 2 رکوردهای 13 تا 22 رو میبینیم و درواقع رکوردهای 11 و 12 به صفحه 1 منتقل شدن. اینطوری، کاربر اصلاً دوتا از رکوردها رو ندیده. مگه اینکه دوباره به صفحه اول برگرده. البته میشه این مورد رو به کاربر اطلاع بدیم. مثلاً تعداد رکوردها رو توی سشن بگذاریم و موقع نمایش هر صفحه، تعداد کل رکوردها رو با تعداد موجود در سشن مقایسه کنیم و اگه تفاوتی ایجاد شده بود، به کاربر هشدار بدیم که اطلاعات تغییر کرده و برای نمایش صحیح، دوباره باید به صفحه 1 برگرده. موفق باشید.

eshpilen
دوشنبه 01 اسفند 1390, 09:57 صبح
مثلاً تعداد رکوردها رو توی سشن بگذاریم و موقع نمایش هر صفحه، تعداد کل رکوردها رو با تعداد موجود در سشن مقایسه کنیم و اگه تفاوتی ایجاد شده بود، به کاربر هشدار بدیم که اطلاعات تغییر کرده و برای نمایش صحیح، دوباره باید به صفحه 1 برگرده. موفق باشید.
فرض کنیم رکورد 3 و 4 حذف شد، اما بعدش دو رکورد دیگه به انتهای جدول اضافه شد.
در این صورت هشدار ما کار نمیکنه :لبخند:
البته جالب اینکه احتمال زیادی داره که دو رکورد جدید در جای رکوردهای حذف شده اضافه بشن (تست کردم). در این صورت دو رکورد جدید به کاربر جاری نمایش داده نمیشن (که البته شاید مشکلی نباشه). بهرحال بنظرم نمیشه و نباید روی این قضیه اتکا کرد (یعنی ممکنه به علتی در جای رکوردهای حذف شده درج نشن).

MMSHFE
دوشنبه 01 اسفند 1390, 14:59 عصر
خوب پس میتونیم حداقل فقط آیدی رکوردها رو بخونیم و توی سشن بگذاریم (اینطوری فقط یک آرایه یک بعدی توی سشن داریم). بعد موقع نمایش صفحه، دوباره آرایه آیدی ها رو بخونیم و با محتوای سشن مقایسه کنیم و اگه فرق داشتن، یعنی اینکه اطلاعات تغییر کرده. موفق باشید.

eshpilen
دوشنبه 01 اسفند 1390, 21:10 عصر
این هم روش جالبیه که ظاهرا این مشکلات رو نداره: این تاپیک (http://forum.iranphp.org/Thread-%D8%B5%D9%81%D8%AD%D9%87-%D8%A8%D9%86%D8%AF%DB%8C-%D8%A8%D8%B1-%D8%B1%D9%88%DB%8C-%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D8%A8%D8%B2%D8%B1%DA%AF)
البته بنده برای جاهایی که حساسیت بالایی ندارن پیشنهاد نمیکنم که از روشهای پیچیده تر استفاده بشه فقط به همین خاطر.