שלח תשובה

זירת השאלות

646
צפיות
11
תשובות

מישהו יכול להגיד לי מה בעיה פה ?

,‏ 30 באפריל, 2006

אשמח אם מישהו יסביר לי מה הבעיה בפונקציה הבאה שיצרתי, משום מה היא מעמיסה על האתר ולוקח לו המון זמן לעבוד עם הפונקציה הזאת


' פונקציה השולפת את תתי הקטגוריות מקטגוריה מסויימת
function SectorFind1(index, txt, mone)
    Dim rsNavigation, sqlQueryNav, Navigation, checkNavigation
    set rsNavigation = server.CreateObject ("ADODB.RecordSet")
    sqlQueryNav =  "SELECT ID_Sector,ParantID,SectorName FROM Sectors where ParantID="& index &" Order By ID_Sector"
    rsNavigation.Open sqlQueryNav,DSN,0,1
        do until rsNavigation.EOF
            if mone="1" then
                txt = txt & rsNavigation.Fields("ID_Sector")
                mone = mone+1
            else
                txt = txt & ", " & rsNavigation.Fields("ID_Sector")
            end if
            checkNavigation = rsNavigation.Fields("ID_Sector")
                call SectorFind1(checkNavigation, txt, mone+1)
        rsNavigation.MoveNext
        loop
SectorFind1 = txt
end function


זאת פונקציה שמקבל מספר מזהה של קטגוריה, ואז היא שולפת את כל תתי הקטגוריות שנמצאות מתחת לקטגוריה שצוינה.
אשמח לקבל עזרה בהבנה מה הבעיה בפונקציה הזאת, וכיצד ניתן לפתור את הבעיה.

תגיות:

11 תשובות

  1. gilad123 הגיב:

    תשובה
    לגבי הלמה, הפונקציה נקראת בריקורסיה (דבר שהוא הגיוני בסריקת עץ), ריקורסיה נוטה לאכול משאבים (בעיקר במחסנית הקריאות שבדרך כלל די מוגבלת).
    כל קריאה ריקורסיבית יוצרת  עוד אובייקט RecordSet שנשאר פתוח

    מה לעשות?
    הייתי בודק כמה פעמים הפונקציה נקראת (שידפיס משהו כל פעם) והאם המספר הזה הגיוני לעומק ורוחב העץ (שמן הסתם עוד יגדל בעתיד)
    לגבי ה RecordSet:
    כדאי מאוד לא להשאיר RecordSet'ים פתוחים
    הייתי משתמש ב GetRows (יש מאמר באתר) כדי לשחרר את ה RecordSet ולשמור רק מערכים שהם הרבה יותר קלים.
    הייתי בודק את ההשפעה של ליצור RecordSet אחד בלבד מחוץ לפונקציה, מה שהפונקציה תעשה זה לקבל אותו כפרמטר (ByRef כמובן) למלא אותו (open), להעביר את התוכן למערך (GetRows) לסגור אותו, ולשלוח אותו סגור בקריאה הריקורסיבית הבאה, הקצאה של RecordSet בזיכרון וניקוי אחריו זה דבר יקר.

    עוד משהו, אם יש שם הרבה שירשורים, הייתי בודק את ההשפעה של ליצור ADODB.Stream (כמובן ליצור אחד מחוץ לפונקציה שישלח ByRef) שיחליף את אדון txt

    וכיוון אחר, (לא בטוח שאפשר) אבל מכיון שמה שמוחזר בסופו של דבר זה רק סטרינג, הייתי בודק את האפשרות לעשות את הכל בתוך ה BD

  2. gilad123 הגיב:

    ועוד משהו
    חסרות לי השורות

    rsNavigation.close
    Set rsNavigation = Nothing

    שמשחררות משאבים

  3. Shuki142 הגיב:

    לא אפשרי
    יוצר לי באגים שאני מוסיף את השורות האלו.
    מהסיבה שלאחר מכן שהוא קורא לפונקציה פעם נוספת לא ניתן לעבוד על המשתנה משום שהוא סגור.

  4. Shuki142 הגיב:

    תגובה
    לגבי המספר שהיא רצה, בדקתי והיא רצה המון, עשרות אלפי פעמים וזה הדבר שגורם לאיטיות אני מדע לזה, השאלה שלי כיצד אני יכול לייעל את זה.
    זה רץ מלא פעמים על רשומות שלא קשורות בכלל לקטגוריה שאני מבקש.

    לגבי השיטות שלך לא ממש הבנתי את הדרכים שניסית להסביר לי, אשמח אם תפרט קצת או יותר או שתביא לי דוגמא.

  5. gilad123 הגיב:

    אני מכוו שזה מספיק ברור
    המבנה של השאילה נראה הגיוני (חוץ מ SectorName שלא משתמשים בו), תנסה לבדוק שאין שגיאות בעץ עצמו (לדוגמא הגדרה מעגלית א -> ב -> ג-> א) דבר שירוץ עשרות אלפי פעמים עד שיגמר המקום במחסנית.
    לפעמים אין ברירה אלא לעבור ידנית על הקריאות (הראשונות) ולראות למה הוא קרא להם, זה משעמם פחד אבל יעיל  

    מה שהתכוונתי


    .
    .
    dim rsNavigation
    set rsNavigation = server.CreateObject ("ADODB.RecordSet")
    Call SectorFind1(index, txt, mone, rsNavigation)
    Set rsNavigation = Nothing
    .
    .
    function SectorFind1(index, txt, mone, ByRef rsNavigation)
       dim arr,i
       sqlQueryNav =  "SELECT ID_Sector,ParantID,SectorName FROM Sectors where ParantID="& index &" Order By ID_Sector"
        rsNavigation.Open sqlQueryNav,DSN,0,1
        arr = rsNavigation.GetRows
        rsNavigation.close
        For i=0 to UBound(arr,2)
        .
        .
        call SectorFind1(checkNavigation, txt, mone+1,rsNavigation)
        Next
    end function
    .
    .

    אם אתה לא מכיר את העבודה עם GetRows  קרא את המאמר הזה

    כמובן לזה תוסיף את הלוגיקה שלך

    הרעיון הוא שאתה מגדיר רק RecordSet אחד בזיכרון, ואתה לא משאיר יותר מ RecordSet אחד פתוח בכל זמן נתון

  6. Shuki142 הגיב:

    אני מצרף את הפונקציות לצפיה
    זה עובד אבל יותר איטי ממה שהיה קודם.
    אני מצרף את הפונקציות שאני עובד איתם כידי לקבל את מה שאני רוצה.
    הפונקציה הראשונה והראשית מקבלת מספר ID ומחזירה בסופו של דבר ערך שאני צריך לגבי ה ID הזה, הפונקציה הזאת נעזרת בפונקציה הבעיתית שאני מציג פה, הפונקציה אמורה לשלוף את כל תתי הקטגוריות שיש מתחת לקטגוריה שנשלחה לבדיקה.

  7. gilad123 הגיב:

    תבדוק את מבנה העץ
    עץ כזה הוא קצת כאב ראש לתחזוקה (הוספת הורדת ושינוי מיקום של צמתים), תנסה לבדוק האם המבנה שלו לא נהרס, אם אין לך לולאות בתוך העץ, נסה להתחקות אחר סדר הקריאות של הפונקציה, נסה לקרוא לפונקציה עם צאצא מהדור האחרון ותראה שהיא לא קוראת לאף אחד מתחתיו, תעלה בהדרגה דורות למעלה ותראה שהיא קוראת רק למי שהיא צריכה, או לחילופין – איפה היא מתחילה להשתולל, אם תדע לשים את האצבע על איפה, זה יעזור בלהבין את הלמה.
    אתה יכול לנסות להדפיס את השאילתה כל פעם למסך, להריץ אותה ישירות מול ה DB ולראות שהתוצאה הגיונית.

    וכיוון אחר, מספר הדורות המאקסימאלי בעץ הוא נתון קבוע וסביר? אם כן הייתי שוקל לוותר על הריקוסיה ולבנות שאילתה אחת שתבצע איחוי של הטבלה לעצמה (כמספר הדורות)

  8. Shuki142 הגיב:

    בניתי פונקציה חדשה
    בניתי פונקציה חדשה שמתבססת על מערך, עובד מצויין אך עדיין יש איטיות, שאני יודע בדיוק ממה היא נובעת.
    הבעיה נובעת מכך שכל פעם הפונקציה מחזירה את המשתנה mone אני רוצה שהיא תחזיר אותו פעם אחת בסוף, שהיא תסיים לבדוק את כל הקטגוריות.
    זה אפשרי ? כיצד עושים את זה ?

    הפונקציה היא:

    function showSector(index, mone)
        dim a
        for a=0 to uBound(allSectorArray,2)
            if allSectorArray(1,a) = index Then
                mone = mone & ", " & allSectorArray(0,a)
                showSector = mone
                call showSector(allSectorArray(0,a),mone)
            end if
        next
    end function

  9. gilad123 הגיב:

    למה להכניס אותו לריקורסיה?
    למה אתה בכלל שולח אותו כל פעם מחדש?
    הרעיון של הריקורסיה שלכל קריאה יש את המשתנים שלה (שזה מה שאתה עושה עם index), אם אתה בכל מקרה שולח את mone בשלמותו כל פעם מחדש אז למה שלא תגדיר mone אחד מחוץ לפונקציה (או sub) שלא ישלח כלל אל הפונקציה או שישלח ByRef (מקובל לומר שמשתנה גלובלי זה יותר איתי, לא בדקתי), אין טעם להעתיק אותו כל פעם לתוך הפוונקציה (showSector = mone)
    בסופו של דבר הערך יהיה בתוך המשתנה החיצוני.

    אם יש לך הרבה שירשורים, ADODB.Stream  עושה את זה יותר מהר (בתנאי שאתה מגדיר אחד ולא כל פעם עוד אחד)

  10. Shuki142 הגיב:

    אז ככה…
    ניסתי להגדיר את mone כגלובאלי וזה לא עובד כמו שזה צריך לעבוד.
    תבין שאני קורה לפונקציה הזאת היא אמור להחזיר לי מחרוזת מסויימת שאותה אני מציב בתוך שאילתה.
    עכשיו לגבי מה שרשמת showSector = mone השורה הזאת בעצם מחזירה כל פעם את הערך שיש במונה לא מעתיקה את מונה לפונקציה.
    מה שאני שואל, זה כיצד אני יכול לעשות שהמשפט הזה showSector = mone יקרה רק בסיומה של הפונקציה, זאת אומרת שהופנקציה מסיימת את הפעולה שלה (עברה על כל הקטגוריות), רק אז הערך של מונה ישלח, הערך בעצם ישלח פעם אחת ולא כל פעם מחדש, שזה מה שמאט את הפונקציה.

    לגבי ADODB.Stream אין לי מושג לגבי הנושא הזה, אם תוכל להסביר לי או לכוון אותי לחומר על נושא זה אשמח לקבל מידע נוסף.

שלח תשובה