ورود

View Full Version : مشکل با لود نمودن رکوردهای زیاد در data grid



ali190
سه شنبه 24 اسفند 1389, 17:36 عصر
باسلام و عرض خسته نباشید
من میخوام در برنامه ام تعداد 1 میلیون رکرود رو با adodc بگیرم و در یک datagrid نمایش بدم
اما این کار هم سرعتم رو پایین میاره و احتمال داره کاربران رو در شبکه با مشکل مواجه کنه
میخواستم بدونم شما برای اینکه بتونم این تعداد رکورد رو بدون افت سرعت نمایش بدم چه راهکاری رو پیشنهاد میکنید؟
آیا اصولاً خود adodc با تعداد زیاد رکورد مشکل نداره؟
ممنون میشم راهنمایی بفرمائید
یاعلی

ali190
سه شنبه 24 اسفند 1389, 23:31 عصر
باسلام
دوستان فرض بفرمائید بنده در دیتابیسم نام و نام خانودگی حدود 4.000.000 نفر رو دارم
حالا میخوام نام افرادی رو که اسمشون "علی" است رو بیرون بکشم و در دیتاگرید نمایش بدم
فرض بفرمائید 400.000 نفر نامشون "علی" هست و من میخوام این رکوردها رو در یک data grid نمایش بدم
فرض بفرمائید من در داخل فرمم یه دکمه Next گذاشتم و میخوام از این 400.000 رکورد 1000 تا 1000 تا تو data grid ام نمایششون بدم و با زدن دکمه Next برم رو 1000 تای دوم و همینطور الی آخر
این کار رو برای این میخوام انجام بدم که یک دفعه حجم عظیمی از رکوردها در datagrid لود نشه که بعداً مشکلات افت سرعت و... رو بوجود بیاره
آیا یک همچین چیزی عملی هست؟
ممنون میشم راهنماییم کنید
یاعلی

mpmsoft
چهارشنبه 25 اسفند 1389, 09:54 صبح
شما برای صفحه بندی می تونید از دستور زیر استفاده کنید (برای SQL 2005 به بالا)

SELECT Description, Date
FROM (SELECT ROW_NUMBER() OVER (ORDER BY Date DESC)
AS Row, Description, Date FROM LOG)
AS LogWithRowNumbers
WHERE Row >= 1 AND Row <= 10

ali190
شنبه 28 اسفند 1389, 14:37 عصر
سلام
دوستالن اگر میشه من رو راهنماییم کنید
چطور میتونم اطلالعاتم رو تیکه تیکه لود کنم؟

M_Maskout
یک شنبه 29 اسفند 1389, 09:56 صبح
سلام
یه فیلد Autonumber تو جدول قرار بدین، و بر اساس اون جدول رو 67703 کنید.
خاصیت CursorLocation رو به adUseServer تغییر بدین تا عملیات فیلتر کردن، در سرور انجام بشه و شما فقط هر تعداد رکورد که مورد نظر دارین روی شبکه جابجا کنید.


آیا یک همچین چیزی عملی هست؟

احتمالاً این تنها و بهترین راهه، خاصیت 67703 می‌تونه به شما کمک کنه.
مثلاً اگر یه فیلد ID از نوع AutoNumber تو جدول داشته باشین:

rsTable1.67703 = CStr(1001) + " <= ID " + CStr(2000)

توجه: کلمه 67703 رو بخاطر محدودیت‌های قانونی کشور به صورت عکس گذاشتم.

ali190
یک شنبه 29 اسفند 1389, 10:22 صبح
سلام
از توضیحاتتون خیلی تشکر میکنم
ولی این روش یه مقدار منعطف نیست
برای مثال:
فرض بفرمائید من یه دیتابیس دارم که نام ، محل تولد ، سن افراد مختلف رو قراره درش ذخیره کنم
نام افرادی که اسمشون "علی" هست حدود 30000 نفر است (کل افراد ثبت شده حدود دو میلیون نفر است)
من میخوام افرادی رو که اسمشون علی است رو فیلتر کنم و میخوام این لودینگ بصورت 1000 تا 1000 رکورد در دیتاگریدم نمایششون بدم ، وبا زدن روی رکمه Next به 1000 رکورد بعدی برم و الی آخر
در اینجا به هیچ وجه دیگه استفاده از ID Number کاربردی نداره ، چون قاعده تسلسل رکوردها در این دیتابیس وجود نداره
من یه بار احتمال داره نام علی رو در رکورد 4 ، 8 ،1024 وارد کنم ، یه بار هم احتمال داره نام علی رو در رکوردهای 5، 88 ، 1024 وارد کنم
امیدوارم منظورم رو رسونده باشم
یاعلی

M_Maskout
یک شنبه 29 اسفند 1389, 10:45 صبح
هنوز یه راه هست؛ شما باید:
1. ID رکورد جاری رو بخونید.
2. با استفاده از متد Move، به تعداد لازم تو دیتابیس جلو برید.
3. ID رکورد فعلی رو بخونید.
و در آخر از همون دستور قبلی استفاده کنید:

rsTable1.http://barnamenevis.org/attachment.php?attachmentid=67703&d=1300603868 = CStr(ID1) + " <= ID " + CStr(ID2)

ali190
یک شنبه 29 اسفند 1389, 10:48 صبح
سلام
آقا مهدی اجازه بدید بنده یه مثال (پروژه نمونه) بسازم و بر روی اون پروژه بحث کنیم
آیا روش پیشنهادی شما در فیلترهای چندگانه (فیلتر نمودن رکوردها با شروط مختلف ) هم جواب میده؟
ممنون و متشکر از توضیحات شما
یاعلی

M_Maskout
یک شنبه 29 اسفند 1389, 11:05 صبح
آره! مشکلی نداره.
مثلا:
اول دستور زیر اجرا می‌شه.

rsTable1.http://barnamenevis.org/attachment.php?attachmentid=67703&d=1300603868 = "Name LIKE 'علی' "

بعد پروسه‌ای که گفتم ، طی می‌شه.
بعد هم یه چیزی شبیه به این

rsTable1.http://barnamenevis.org/attachment.php?attachmentid=67703&d=1300603868 = CStr(ID1) + " <= ID " + CStr(ID2) + " AND Name LIKE 'علی' "

به فاصله‌ها حتماً دقت کنید؛ حتماً بعد قبل از AND فاصله لازم هست.

ali190
یک شنبه 29 اسفند 1389, 13:22 عصر
باسلام
من در سورس زیر میخوام رکوردهایی که اسمشون "علی" است رو 10 تا 10 در دیتاگرید بریزم و لود کنم
من به دنبال روشی هستم که منعطف باشه
ممنون میشم راهنماییم کنید
یاعلی

M_Maskout
سه شنبه 02 فروردین 1390, 10:02 صبح
سلام
آقا مهدی اجازه بدید بنده یه مثال (پروژه نمونه) بسازم و بر روی اون پروژه بحث کنیم
آیا روش پیشنهادی شما در فیلترهای چندگانه (فیلتر نمودن رکوردها با شروط مختلف ) هم جواب میده؟
ممنون و متشکر از توضیحات شما
یاعلی

سلام؛
دوست عزیز پروژه نمونه‌ی شما که هیچ کاری نمی‌کنه، اصولاً برای نمونه بهتره مقداری از مسیری که رفتی رو نشون بدی (مثلاً فیلتر سازی‌ای که خود انجام دادی) تا بتونی از راهنمایی همه دوستان استفاده کنی، نه اینکه فقط یه فرم با دو تا دکمه که هیچ کاری انجام نمی‌ده چون در این صورت مخاطب شما فقط بنده می‌شم (که تو پست‌های قبلی روشم رو گفتم).
به هر حال پیاده سازی طرحی که تو پست #9 ارائه دادم رو به پروژه نمونه شما اضافه کردم و کار می‌کنه، هر چند اصولاً مخالفم که کد نوشته شده رو صریحاً به کسی بدم (صرفاً به جهت اینکه خلاقیت اون رو ازش می‌گیره و نه چیز دیگه) ولی با اینحال برنامه شما می‌تونه از کدی شبیه این استفاده کنه:

Public ID1 As Integer, ID2 As Integer
Enum MoveStateEnum
Forward = 1
Backward = 2
End Enum
Sub MakePage(MoveState As MoveStateEnum)
If MoveState = Forward Then
Adodc1.Recordset.Filter = "nam = 'Úáí' AND ID > " + CStr(ID2)
If Adodc1.Recordset.RecordCount > 0 Then
ID1 = Adodc1.Recordset!ID
Adodc1.Recordset.move 9
If Adodc1.Recordset.EOF Then Adodc1.Recordset.MoveLast
ID2 = Adodc1.Recordset!ID
End If
Else
Adodc1.Recordset.Filter = "nam = 'Úáí' AND ID < " + CStr(ID1)
If Adodc1.Recordset.RecordCount > 0 Then
Adodc1.Recordset.MoveLast
ID2 = Adodc1.Recordset!ID
Adodc1.Recordset.move -9
ID1 = Adodc1.Recordset!ID
End If
End If
Adodc1.Recordset.Filter = "nam = 'Úáí' AND ID >= " + CStr(ID1) + " AND ID <= " + CStr(ID2)
End Sub
Private Sub Command1_Click()
MakePage Forward
End Sub
Private Sub Command2_Click()
MakePage Backward
End Sub
Private Sub Form_Load()
Adodc1.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & App.Path & "\Database2.mdb;"
Adodc1.RecordSource = "SELECT * FROM Table1"
Adodc1.Refresh
MakePage Forward
End Sub

ali190
سه شنبه 02 فروردین 1390, 11:00 صبح
سلام
ابتدا تشکر میکنم از پاسخ استادانه شما
عذرخواهی میکنم بایت اشتباهی که از جانب بنده رخ داد ، حق با شماست ،نمونه پروژه ام خیلی ابتدایی بود
امکانش هست راجع به کدهای زیر یه مقدار توضیح بفرمائید:

Public ID1 As Integer, ID2 As Integer
Enum MoveStateEnum
Forward = 1
Backward = 2
End Enum
Sub MakePage(MoveState As MoveStateEnum)
If MoveState = Forward Then
Adodc1.Recordset.Filter = "nam = 'Úáí' AND ID > " + CStr(ID2)
If Adodc1.Recordset.RecordCount > 0 Then
ID1 = Adodc1.Recordset!ID
Adodc1.Recordset.Move 9
If Adodc1.Recordset.EOF Then Adodc1.Recordset.MoveLast
ID2 = Adodc1.Recordset!ID
End If
Else
Adodc1.Recordset.Filter = "nam = 'Úáí' AND ID < " + CStr(ID1)
If Adodc1.Recordset.RecordCount > 0 Then
Adodc1.Recordset.MoveLast
ID2 = Adodc1.Recordset!ID
Adodc1.Recordset.Move -9
ID1 = Adodc1.Recordset!ID
End If
End If
Adodc1.Recordset.Filter = "nam = 'Úáí' AND ID >= " + CStr(ID1) + " AND ID <= " + CStr(ID2)
End Sub
ممنون و متشکر از لطف شما
یاعلی

M_Maskout
سه شنبه 02 فروردین 1390, 14:21 عصر
سلام
ممنون از ابراز لطف شما
بر اساس شماره گذاری سطرها در پست 11#:
سطر 1: تعریف دو متغیر سراسری ID1 و ID2 برای نگهداری حدود بالایی و پایینی صفحه بر اساس فیلد ID از جدول
با این دو تا متغیر در هر زمان می‌شه فهمید سطر ابتدایی و انتهایی dbgrid، کدوم رکورد از جدول رو نشون می‌ده که برای حرکت صفحه به صفحه تو جدول لازمه
سطرهای 2 تا 5: تعریف یک نوع شمارشی صرفاً برای شفاف تر شدن برنامه؛ همونطور که می‌دونید، نوع‌های شمارشی فقط به این خاطر ایجاد می‌شن که اولاً به مقادیر مورد نظر، یه اسم با معنی داده بشه (مثلاً در اینجا Forward = 1) و دیگه اینکه در زمان استفاده کردن از متغیرهای دارای اون نوع، محدوده مقادیری که اون متغیر به خودش می‌گیره، معلوم باشه و با وارد کردن مقادیر غیر مجاز، برنامه دچار اشکال نشه.
سطر 6: روتین اصلی صفحه بندی جدول با تنظیم حالت وضعیت حرکت (MoveState)؛
اصولاً برای حرکت بین صفحه‌ها دو حالت در نظر گرفته شده (این دو حالت همونایی هست که تو سطر 3 و 4 مشخص شده) که تو سطر 7 راجع به انتخاب وضعیت تصمیم گیری می‌شه.
اگر حرکت به سمت جلو باشه سطرهای 8 تا 13، و اگر به سمت عقب (صفحه قبلی) باشه سطرهای 16 تا 22 اجرا می‌شه.
سطرهای 8 تا 13:
اول جدول رو بر اساس نام مورد نظر (که اینجا درست نشون داده نشده و همون "علی" هست) و مقدارهای بزرگتر از ID2 فیلتر می‌کنیم. توجه به این نکته لازم هست که مقدار ID2 در ابتدای اجرای برنامه صفر هست و عبارت عملاً تبدیل می‌شه به ID > 0 که یعنی کل جدول. بعد هم (سطر 9) در صورتیکه این فیلتر خروجی داشت (به انتهای جدول نرسیده بودیم) کار ادامه پیدا می‌کنه.
سطر 10: بعد از اینکه جدول فقط نام مورد نظر رو نشون داد، مقدار ID اولین رکورد رو تو ID1 ذخیره می‌کنیم و یک واحد کمتر از تعداد لازم (در اینجا 9=1-10 رکورد) با استفاده از متد Move به جلو حرکت می‌کنیم. (سطر 12) اینجا تست می‌کنیم که جدول به انتها رسیده یا نه. اینکار به این خاطر هست که ممکنه تعداد کل رکوردهای دارای نام "علی"، مضربی از طول صفحه نباشه و باعث بشه برنامه خطا بده و حالا (سطر 13) دوباره ID رکورد به دست اومده رو ایندفعه تو ID2 ذخیره می‌کنیم.
حالا برنامه به سطر 24 می‌پره؛
سطر 24: تو این سطر ما دو تا مقدار معلوم داریم، ID1 و ID2 که با احتساب "علی" = nam، فاصله دلخواه (مثلاً 10 رکورد) از هم رو دارند و کافیه کل جدول رو به کمک اونا فیلتر کرد. و همین؛ کار تموم شد.
البته راجع به سطرهای 16 تا 22، توضیحات مشابهی وجود داره فقط این بار، قرار هست که به سمت بالای جدول حرکت انجام بشه. بخاطر همین هم (سطر 16) فیلتر اولیه رو بر اساس ID1 ایجاد می‌کنیم. چون ID1، مقدار فیلد ID از رکورد ما بعد آخر صفحه قبل رو نشون می‌ده. این بار رکوردهایی رو در نظر می‌گیریم که IDی اونا از ID1 کوچکتر هست. (سطر 17) بعد در صورتیکه تعداد رکوردهای خروجی از فیلتر، بیشتر از صفر باشه (ویژگی RecordCount این مقدار رو مشخص می‌کنه و یعنی اینکه هنوز صفحه‌ای قبل از صفحه‌ی فعلی وجود داره)، (سطر 18) ابتدا به آخر جدول فیلتر شده حرکت می‌کنیم (دلیل این کار اینکه با هر بار اجرای فیلتر مکان نما جدول به رکورد ابتدایی اشاره می‌کنه) و (سطر 19) ID رکورد به دست آمده رو تو ID2 به عنوان رکورد انتهایی صفحه ذخیره می‌کنیم. بعد (سطر 20) به اندازه تعیین شده (باز همان 9=1-10 رکورد) به کمک متد Move به عقب برمی‌گردیم (به علامت منفی تو این سطر توجه کنین). در آخر (سطر 21) ID رکورد به دست آمده رو تو ID1 به عنوان رکورد ابتدایی صفحه می‌ریزیم. و بعد هم که همون سطر 24 هست.
حالا از این روتین هرجا که لازم بود (در وقفه کلیک دکمه‌ها و حتماً در روتین Load فرم) استفاده می‌کنیم.

توجه مهم: ممکنه هنوز Load فرم، زمان بر باشه که در این صورت پیشنهاد می‌دم مقدار ویژگی DataSource از DataGrid1 رو در زمان طراحی خالی کنید و بعد از فراخونی روتین بالا (در اینجا بعد از سطر 36) اون رو مقدار دهی کنین. (با این دستور : Set DataGrid1.DataSource = Adodc1)

یه نکته؛ به نظر من و فقط به نظر من استفاده از کنترل‌های آگاه از داده (Data aware control)، هرچند که باعث راحتر شدن برنامه نویسی می‌شه، اما کیفیت برنامه رو پایین می‌یاره. من ترجیح می‌دم (و فقط ترجیح می‌دم!) از کنترل‌های معمولی استفاده کنم و در زمان اجرا خودم همه کارا رو بکنم.

ali190
سه شنبه 02 فروردین 1390, 17:13 عصر
سلام
ممنون از M_Maskout به خاطر پاسخ دقیق و توضیحات تکمیلیشون
من هدفم از نمایش تکه به تکه رکوردها کاهش میزان استفاده از فضای رم و جلوگیری از نمایش حجم زیادی از اطلاعات است ، یا بهتره این طور بگم ، آیا اتخاذ این روش میتونه در آزاد کردن فضای Ram کمکی بکنه؟
آیا این روش میتونه روش مناسب و درستی باشه ؟
ضمناً میخواستم در خصوص خاصیت CursorLocation کنترل adodc که حاوی دو مقدار adUseServer و adUseClient هست یه مقدار راهنماییم کنیدکه چه فرقی با هم میکنند؟
یه سئوال هم داشتم راجع به استفاده از "+" در سینتکس دستور زیر :

Adodc1.Recordset.Filter = "nam = 'Úáí' AND ID >= " + CStr(ID1) + " AND ID <= " + CStr(ID2)

ممنون و متشکر از لطف شما
یاعلی

M_Maskout
سه شنبه 09 فروردین 1390, 16:50 عصر
من هدفم از نمایش تکه به تکه رکوردها کاهش میزان استفاده از فضای رم و جلوگیری از نمایش حجم زیادی از اطلاعات است ، یا بهتره این طور بگم ، آیا اتخاذ این روش میتونه در آزاد کردن فضای Ram کمکی بکنه؟

هر سطر از یه grid (از هر نوع که باشه) یه نمونه از یه کلاسه؛ برای نمونه سازی از یه کلاس ابتدا یه مقدار حافظه برای ساخت کلاس و ویژگی‌های (= properties) غیر پویای اون (مثلاً رنگ، ابعاد و چیزایی از این دست) در نظر گرفته می‌شه، بعد در ادامه برای ویژگی‌های پویا کلاس (مثل caption یا در مورد گرید محتوای هر سلول) جافظه اختصاص پیدا می‌کنه. و چون کلاس‌ها کلاً در حافظه پویا نمونه‌سازی می‌شن، یه حافظه هم که خیلی مقدار ناچیزیه، برای اشاره به آدرس کلاس(های) ساخته شده در نظر گرفته می‌شه.
معنی تمام این حرفا اینه که هرچی تعداد سطرهای grid کمتر باشه، حافظه کمتری اشغال می‌شه (اشغال حافظه کمتر، اجرای برنامه سریعتر). از طرفی با لود کردن یه جدول تو حافظه، اصولاً برای تمام اطلاعات جدول، حافظه در نظر گرفته می‌شه (شاید به همین خاطر هست که باز کزدن جداول با تعداد رکورد خیلی زیاد، به کندی انجام می‌شه).

آیا این روش میتونه روش مناسب و درستی باشه ؟
این روش، روش درستیه، اما اینکه بهترین باشه، یه کم ...؛ شاید اگه فقط هدف، نمایش اطلاعات باشه و صرفه جویی تو حافظه به طرز خیلی چشمگیری مهم باشه، بشه عوض استفاده از grid، از یه picturbox استفاده کرد و خطوط grid رو با دستور مثلاً Line توی اون کشید، و اطلاعات سلول‌ها رو هم با دستور Print توی اون نوشت و کلی کد نویسی دیگه...؛ ولی قطعاً حافظه کمتری از قبل استفاده می‌شه.


ضمناً میخواستم در خصوص خاصیت CursorLocation کنترل adodc که حاوی دو مقدار adUseServer و adUseClient هست یه مقدار راهنماییم کنیدکه چه فرقی با هم میکنند؟

خاصیت CursorLocation، یه راهکار خوب برای صرفه جویی تو استفاده از حافظه، مخصوصاً تو زمان استفاده از روش به کار رفته بالا و مخصوصاً (مخصوصاً بیشتر!) تو زمان استفاده از برنامه تو حالت کلاینت/سرور، هست.
adUseServer: باعث می‌شه که جدول فقط باز بشه و هر زمان که به هر تعداد رکورد احتیاج بود (با استفاده از دستورات SQL) اون مقدار از جدول تو حافظه لود بشه. اشکال استفاده از اون هم اینه که اولاً تو تعداد بالای کلاینت شبکه (و البته استفاده همزمان چند کاربر از برنامه)، بار کاری سرور زیاد می‌شه و بنابراین افت سرعت شبکه به طور محسوسی اتفاق می‌افته، ثانیاً یه سری از توانایی‌های دسترسی به داده‌های جدول (در رأس اونا مرتب کردن یا همون Sort) بعد از باز شدن جدول غیر قابل استفاده‌ست، دیگه اینکه برای اینکه هر زمان بشه تو جدول به عقب و جلو رفت، باید نوع cursor هم به adOpenDynamic تغییر کنه، که خود این باعث کند شدن دسترسی به جدول می‌شه، (هرچند که این نوع از cursorها همونطور که از اسمشون پیداست، خاصیت پویایی دارن و بنابراین تو استفاده از حافظه صرفه جویی می‌کنن) و یه سری دردسرهای دیگه. بهترین کاربرد adUseServer تو زمان گزارش گیری (report) از یه جدول با یه query از پیش ذخیره شده (stored procedure) هست. که فقط یه بار و به اندازه لازم جدول باز می‌شه و حافظه اشغال می‌کنه.
شما برای استفاده از adUseServer باید حتماً grid رو دستی پر کنی.
adUseClient: باعث می‌شه کل جدول تو دستگاه محلی (کلاینت و نه سرور) لود بشه، که هم سرعت دسترسی به جدول خیلی خیلی بالا می‌ره، هم محدوده دسترسی اضافه می‌شه (همون مرتب سازی و بقیه چیزا). به هر حال بارگذاری یه جدول گنده تو حافظه با این روش (adUseClient) زمان بر هست و کاریش هم نمی‌شه کرد. تو پروژه شما، پیشنهاد من، همین adUseClient هست؛ چون برای به دست آوردن رکوردهای هر صفحه لازم چندین بار به جلو یا عقب حرکت بشه و عملیات فیلتر انجام بشه که در صورت عدم استفاده از adUseClient، حتی اگر خطایی هم پیش نیاد، بازم کار خیلی کند انجام می‌شه (حتی روی یه دستگاه و در شرایطی که سروری وجود نداره).
راستی اگر قفل کردن یه رکورد یا جدول در زمان‌هایی در استفاده از برنامه (مثلاً به دست آوردن شماره قبض تو یه برنامه‌ی فروش با چندین صندوق) مهم باشه adUseServer تنها راه کاره.


یه سئوال هم داشتم راجع به استفاده از "+" در سینتکس دستور زیر :

Adodc1.Recordset.Filter = "nam = 'Úáí' AND ID >= " + CStr(ID1) + " AND ID <= " + CStr(ID2)


VB یکی از راحت ترین سینتکس‌ها رو داره که از QuickBasic به ارث برده. برای متصل کردن دو رشته علاوه بر &، می‌شه از + استفاده کرد، هیچ فرقی هم با هم ندارن، منتها استفاده از &، خوانایی برنامه رو بالا می‌بره.