.התחלנו כבר לדבר על דיפדוף יעיל בדטה בייס בעבר, עכשיו נמשיך את זה לצד הלקוח
נשתמש באג'קס ליישום הדיפדוף (אני משתמש ב framework לנוחות, אבל כל אג'קס יעשה את העבודה)
נתחיל בבניית הטבלה בדף, אנחנו כבר יודעים איך למשוך את הנתונים כך שנקבל רק את השורות המתאימות לדף שלנו, אז פשוט נזרוק את כל הנתונים לדף בצורת טבלה. את הטבלה נשים בתוך div עם id
נבנה בדף פונקציה שתמשוך לנו את הנתונים מה ADP
private string GetTable()
{
int pagenum;
if (!int.TryParse(Request.Params["pagenum"], out pagenum))
{
pagenum = 1;
}
int rowsPerPage = 10;
string sortByColumn = Request.Params["sortName"];
if (string.IsNullOrEmpty(sortByColumn)) sortByColumn = "id";
string sortOrder = Request.Params["sortOrder"];
if (string.IsNullOrEmpty(sortOrder)) sortOrder = "asc";
DataTable table = ADP.GetUsers(pagenum, rowsPerPage, sortByColumn, sortOrder);
StringBuilder sb = new StringBuilder();
sb.Append("<table><tr><td>id</td><td>name</td></tr>");
foreach (DataRow dr in table.Rows)
{
sb.Append("<tr>");
sb.Append("<td>");
sb.Append(dr["id"]);
sb.Append("</td>");
sb.Append("<td>");
sb.Append(dr["name"]);
sb.Append("</td>");
sb.Append("</tr>");
}
sb.Append("</table>");
return sb.ToString();
}
הפונקציה הזאת בעצם בונה לנו את הטבלה לפי פרמטרים שהיא מקבלת מהדף כמו מספר הדף ולפי מה למיין. אם זו כניסה ראשונה לדף ואין עדיין נתונים היא משתמשת בערכי ברירת מחדל. קבענו כאן גם את מספר השורות להציג בכל דף. כעת ניקח את התוצאה של הפונקציה הזאת ונציב את הסטרינג הזה בתוך הdiv שלנו
<div id="div_data"><%=myTable%>
</div>
כעת נבנה את שורת הדפדוף, אני אשתמש בפשוטה ביותר כרגע. קודם צריכים לדעת כמה נתונים יש בדטה בייס, נמשוך את מספר השורות ונחלק במספר השורות שאנו מציגים בדף כדי לדעת את מספר הדפים
int rowsCount =ADP.GetUsersCount();
int rowsPerPage = 10;
int totalPages=(int)Math.Ceiling((double)rowsCount / rowsPerPage);
הפייג'ר עצמו בדף html:
<%for (int i=0;i<totalPages;i++)
{ %>
<a href="javascript:doPaging(<%=(i+1)%>)"><%=(i+1)%></a>
%<}%>
ופונקציית ג'אווה קטנה
<script type="text/javascript">
var curpage=1;
var sortName='id';
var sortDirection='asc';
function doPaging(pagenum){
curpage=pagenum;
var url='mypage.aspx?action=paging&pagenum='+curpage+'&sortName='+ sortName+'&sortOrder'+sortDirection;
new Ajax.Request(url,{onSuccess:function(t){
if (t.responseText=='error'){
alert('error!');
}else{
document.getElementById('div_data').innerHTML=t.responseText;
}
}});
}
</script>
זהו, בשביל הדפדוף זה מספיק... רק בשביל המיון צריך עוד 2 דברים קטנים לעשות. קודם כל כשאנו בונים את הטבלה, אז בכותרות להוסיף הפניה לפונקציה של ג'אווה סקריפט כדי שכשמקליקים על הכותרת יתבצע מיון
נשנה מעט את איך שבנינו את הטבלה מקודם במקום לכתוב id בכותרת נכתוב <a href=\"javascript:doSort('id');\">id</a> ,וכך לגבי שאר הכותרות, ונבנה את הפונקציה המתאימה בג'אווה סקריפט
function doSort(columnName){
if (sortName==columnName){
if (sortDirection=='asc'){
sortDirection='desc';
}else{
sortDirection='asc';
}
}else{
sortName=columnName;
sortDirection='asc';
}
var url='mypage.aspx?action=paging&pagenum='+curpage+'&sortName='+ sortName+'&sortOrder'+sortDirection;
new Ajax.Request(url,{onSuccess:function(t){
if (t.responseText=='error'){
alert('error!');
}else{
document.getElementById('div_data').innerHTML=t.responseText;
}
}});
}
</script>
הפונקציה הזאת רק מסדרת את הפרמטר של שם העמודה למיון והסדר (עולה יורד) וממשיכה כרגיל כמו הפונקציה הקודמת. זהו בעיקרון, ניתן להוסיף גם חיפוש באותה שיטה למי שרוצה...
להורדת דוגמה (ללא דטה בייס כמובן)
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
Technorati Tags:
sql,
paging
לעיתים אנו רוצים להציג למשתמשים מידע רב מבסיס הנתונים, ולא רוצים להציג את כולו בדף אחד. אז נכון שקיימים הפקדים של הדוט נט שחלקם מכילים כבר אופציה לדיפדוף, אבל זה בעצם דיפדוף "מדומה", הם לא באמת מושכים את המידע שאנחנו צריכים מהדטה בייס, הם מושכים את כל הרשומות, ומציגים לנו את מה שאנחנו צריכים, לכן, אם יש לי מיליון רשומות ואני רוצה להציג 50 כל פעם, אזי כל פעם שאני מדפדף דרך הקונטרול, הוא מושך את כל המיליון רשומות מהדטה בייס ופשוט מציג את ה 50 הבאות. אז השיטה הזו טובה כשרוצים בזריזות לבנות איזה דף עם כמה נתונים, אבל לשרת אמיתי שמשרת עשרות אלפי לקוחות צריך למצוא שיטה טובה יותר.
הכתבה תתרכז בצד של השאילתות לדטה בייס ולא בצד הלקוח. בפעם הבאה נדבר על ישום אג`קסי של הדפדוף ואז גם ניתן יהיה להוריד את הפרוייקט.
השיטה הקלה, אם נתמזל מזלנו ואנחנו לא צריכים אפשרות למיין, או מאפשרים מיון רק לפי עמודה שהיא ייחודית כמו אינדקס, אז פשוט נבנה את הדיפדוף כך שימשוך כל פעם את מספר הרשומות שאנחנו רוצים עם תנאי שמספר האינדקס יהיה גדול (או קטן במקרה של מיון מהגדול לקטן) ממספר כלשהו - האינדקס האחרון מהדף הקודם ( שאותו נשמור בדף או בסשן ). לדוגמא:
ניצור טבלה בשם t1 עם 3 עמודות:
מספר השורות שאנחנו רוצים להציג בדף
const int rowsPerPage=2;
משתנה שמכיל את מספר המזהה של השורה האחרונה שמשכנו (בפעם הראשונה הוא יהיה 0 כמובן):
int lastid=0;
השאילתה :
sql:Sel ect top [rowsPerPage] from t1 where id>[lastid] order by id
MySql : Sel ect * from t1 where id>[lastid] order by id limit [rowsPerPage]
כעת נשמור לנו את המספר המזהה של הרשומה האחרונה שמשכנו -3 את המספר כאמור ניתן לשמור או בסשן (לא מומלץ) או בדף עצמו - למשל בלחצן שמעביר לדף הבא - להוסיף לו את הפרמטר הזה.
כשהלקוח ילחץ על דיפדוף לדף הבא או הקודם, נקבל בשרת את הערך הזה ולפיו נדע איזה רשומות למשוך עכשיו. במקרה שלנו נקבל את המספר 3 לשרת והשאילתה תהיה:
Sql :Sel ect top 2 * from t1 where id>3 order by id
MySql: Sel ect * from t1 where id>3 order by id limit 2
וכך הלאה לשאר הדפים.
החסרונות ברורים:
לא ניתן למיין לפי עמודות אחרות שכן אנחנו חייבים עמודה עם מזהה יחודי
לא ניתן לקפוץ לעמוד מסויים, שכן אנחנו יודעים את האינדקס של העמוד הבא והקודם בלבד
כדי לנסות לפתור את 2 המגבלות האלה, ניתן לשכלל קצת את הפיתרון שלנו כפי שמופיע באתר של מיקרוסופט
בשיטה זו נוכל גם למיין לפי עמודות אחרות וגם נוכל לקפוץ לכל עמוד שנרצה:
נגדיר משתנה שישמור את מספר הדף:
int pageNum=1;
Sql : select top [rowsPerPage] * from (select top [rowsPerPage*pageNum] * from t1 order by name desc) as t2 order by name
Mysql : select * from (select * from t1 order by name desc limit [rowsPerPage*pageNum]) as t2 order by name limit [rowsPerPage]
דבר נוסף שחסכנו זה את הצורך לשמור את האינדקס של העמודה האחרונה, אנחנו ישר בוחרים לפי מספר הדף שהלקוח לחץ עליו. נניח שהוא לחץ על דף מספר 3, ואנו כאמור רוצים להציג 2 שורות בכל דף, אזי השאילתה תהיה :
MySql : sel ect * from (SEL ECT * FROM t1 order by name desc limit 6) as t2 order by name limit 2
Sql :sel ect top 2* from( sel ect top 6 * from t1 order by name desc) as t2 order by name
אולם גם זה פיתרון חלקי ( מיקרוסופט כנראה לא שמה לב...) , כי עבור עמודות עם ערכים זהים, כמו עמודת "מין", תתקבלנה תוצאות מוזרות :
MySql : sel ect * from (SEL ECT * FROM t1 order by gender desc limit 6) as t2 order by gender limit 2
Sql : sel ect top 2 * from (sel ect top 6 from t1 order by gender desc) as t2 order by gender
עדיין אנחנו לא יכולים למיין לפי עמודת "מין"...
אז מה כן עושים? טוב, אז אם יש לנו Sql Server 2005 או MySql הפיתרון יעיל וקל. לא משנה לפי איזו עמודה אני רוצה למיין:
ב sql server 2005 נכתוב
select * ,rank from (select *, row_number() over(order by gender desc) as rank from t1) as t2 where rank between [(pageNum-1)*rowsPerPage+1] and [(pageNum-1)*rowsPerPage+rowsPerPage]
וב MySql הרבה יותר פשוט, פשוט נכתוב
select * from clients limit [(pageNum-1)*rowsPerPage],[rowsPerPage]
והתוצאה כצפוי:
MySql : sel ect * from t1 order by gender limit 2,2
Sql : sel ect *,rank from
(sel ect *,row_number() over(order by gender desc) as rank from t1 ) as t2
where rank between 7 and 8
אבל מה עושים אם אני עובד עם שרת אחר?
אז הפיתרון קצת יותר איטי ויותר מסובך, אבל עדיין יותר יעיל מלמשוך את כל הרשומות אלי לדף...
כל פעם אני רוצה לבצע שאילתה, אני אצור טבלה זמנית, עם עמודה אחת של מיספור אוטומטי ועמודה נוספת שתשמור את מספר האינדקס מהטבלה הראשית.
Create table temp1 (tid int not null primary key auto_increment/identity,foreign_id int)
* אם אני לא סוגר את החיבור לדטה בייס במהלך הפונקציה אז ניתן ליצור את הטבלה בתור טבלה זמנית ואז היא תימחק אוטומטית בעת סגירת החיבור- יותר מהיר ויותר נקי....
כעת נעביר לטבלה הזאת את מספרי האינדקס מהטבלה המקורית, כשהם כבר ממויינים לפי העמודה שאני רוצה:
Insert into temp1 (foreign_id) select id from t1 order by gender
וכעת אני יכול לבחור את מספרי השורות שאני רוצה:
Select * from t1, temp1 where t1.id=temp1.foreign_id and temp1.tid between [(pageNumber-1)*rowsPerPage+1] and [(pageNumber-1)* rowsPerPage+rowsPerPage]
והתוצאה כצפוי:
sel ect t1.* from temp1,t1 where t1.id=temp1.foreign_id and temp1.tid between ((2-1)*2+1) and ((2-1)*2+2) order by gender
רק לא לשכוח בסוף למחוק את הטבלה אם לא השתמשנו בטבלה זמנית...
* יש דרך לשיפור המהירות של השיטה האחרונה באתר 4guysfromrolla
* ניתן להשתמש ב stored procedures ליעול המהירות
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
Technorati Tags:
ajax,
paging
כולנו שמענו על אג'קס אבל בכל זאת החלטתי לתת דוגמה הכי קצרה ופשוטה לקוד ג'אווה סקריפט לכל אלו שרק מכירים את Ajax של Asp ולא ראו את הקוד מעולם. זה טוב לדעת, זה הרבה יותר מהיר ויעיל, ולפעמים גם הדרך היחידה לבצע דברים שבעזרת ה Asp.Ajax סתם אפשר להסתבך
הנה לדוגמה פונקציית ג'אווה סקריפט שמנסה ליצור אובייקט אג'קס לכל דפדפן. הפונקציה מקבלת את הכתובת לפנות אליה, את הפרמטרים להעביר אם יש, ובסוף מעדכנת div על המסך עם התשובה שהיא קיבלה מהכתובת הזאת
<script type="text/javascript">
var ajaxResponse;
var hasError;
function GetAjax(url,parameters){
var myAjax;
try { myAjax = new ActiveXObject('Msxml2.XMLHTTP'); }
catch (e)
{
try { myAjax = new ActiveXObject('Microsoft.XMLHTTP'); }
catch (e2)
{
try { myAjax = new XMLHttpRequest(); }
catch (e3) { alert('could not create ajax object');return; }
}
}
myAjax.onreadystatechange = function()
{
if(myAjax.readyState == 4)
{
if(myAjax.status == 200)
{
hasError=false;
ajaxResponse=myAjax.responseText;
}
else
{
hasError=true;
ajaxResponse='Error ' + myAjax.status;
}
document.getElementById('div_result').innerHTML=ajaxResponse;
}
};
myAjax.open('POST', url, true);
myAjax.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
myAjax.setRequestHeader('Content-length', parameters.length);
myAjax.setRequestHeader('Connection', 'close');
myAjax.send(parameters);
}
</script>
נדאג שיש לנו כזה div בדף
<div id "div_result"></div>
זהו... פשוט צריך לקרוא לפונקציה עם הכתובת והפרמטרים והיא תעדכן את הdiv עם התוצאות
אני אישית משתמש ב prototype framework לשם הנוחות, אבל בכל הדוגמאות ניתן להשתמש בסקריפט הנ"ל באותה צורה
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5