מסד מרובי נתונים

הנה דוגמא

על הטבלה יש מפתח לפי שתי העמודות: Product וPrice.
בתמונה השניה, כללתי את שאר העמודות בinclude, ואז היחס הוא לא 90/10 אלא 100/0 !



 

Miki Watts

New member
Index Scan לאו דווקא אומר שיש בעיה

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

הרצתי שתי שאילתות דומות. אחת עם WHERE column = @myParameter
השניה עם WHERE @myParameter IS NULL OR column = @myParameter
&nbsp
הראשונה רצה עם index seek, ומכאן שהמפתח הוגדר היטב.
השניה רצה עם index scan.
&nbsp
מסקנא: התנאי המורכב פוגע בביצועים, ולכן כאשר יש מסננים אופציונליים אזי ביצועי EF עדיפים מביצועי הפרוצדורה.
&nbsp
מה דעתך?
 

Royi Namir

New member
המסקנה נכונה בהנחה שהכותב לא יודע קורה מאחורי הקלעים.

אחרי מחקר קצר , יצרתי טבלה בדיוק כמו שלך עם הרבה נתוני דמה ,
ואכן קיבלתי בראשון SCAN ובשני SEEK , כמו שאתה קיבלת

http://i.imgur.com/jW4I609.png

אבל אתה יודע למה זה קרה ?
בגלל שבראשון התנאי IS NULL , דופק את האופציה להשתמש ב SEEK.

בשאילתה השניה , אין לו בעיה להשתמש ב INDEX מכיוון שהעמודות מוגדרות ב INDEX.
productID=@productID - כן(!!) מצביע לנקודת התחלה ב INDEX
ואילו
productID=@productID OR @productID IS NULL - לא

לדוגמא : אם @productID הוא NULL , לאיזה ערך בדיוק אתה רוצה שהוא יעשה SEEK ?
והתשובה : אין לאיפה לעשות SEEK , כל הערכים מתאימים
ולכן ה SCAN.

כמו כן , פסחת פה על נקודה חשובה.
אתה רשמת נתונים עם ערך 1 בהתחלה.
SQL SERVER יכול להבין ( ולא להשתמש ב EP קודם , שהיה עם SCAN) שהערך הוא לא NULL
אפשר להגיד לו את זה:

תנסה להוסיף OPTION (RECOMPILE) ואתה תיראה , עכשיו ש SQL תכלס מסתכל על הערך ( שיש לו עכשיו ברגע זה , מבלי זכרונות עבר) - ואתה תראה שהוא משתמש ב SEEK כי הוא מספיק חכם לדעת שעכשיו הוא יכול להשתמש ב SEEK.

http://i.imgur.com/tL4uzGK.png
.
אפשר לקנפג את EF גם שיעשה את זה.

ולכן המשפט שאתה אומר : "כאשר יש מסננים אופציונליים אזי ביצועי EF עדיפים מביצועי הפרוצדורה" - לא נכון תמיד

לא תמיד EF מג'נרט שאילתה אופטימלית.
בכל דבר צריך לחקור ולראות מה קורה במידה ויש בעיה - ואיך לפתור אותה.
בדיוק כמו פה.
 
OPTION (RECOMPILE) זו הנקודה

אתה מבאר באריכות את מה שרמזתי בקצרה על SEEK וSCAN.

הדבר שחידשת הוא OPTION (RECOMPILE).
הכרתי את RECOMPILE בהקשר איפוס הסטטיסטיקות ואופטימיזציה וכו', ולא ידעתי שזה עוזר גם למקרה הזה.
שלימדתני.

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

Royi Namir

New member
יש לציין ש EF

כן יכול להפעיל את זה :

http://stackoverflow.com/questions/25145667/ef-6-parameter-sniffing

ולגבי ה RECOMPILE , נתקלתי במצבים שחייב את זה , לדוגמא :
שאלה ששאלתי מזמן ( לקח לי יומיים להבין למה זה קורה):

http://dba.stackexchange.com/questions/41343/force-sql-server-to-run-query-conditions-as-written

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

24sharon

New member
האמת שמהודעה זו אני איבדתי אתכם

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

ותמהתי עליו. עייני לעיל.
את יכולה למחוק את כל הPredicateBuilder.
 

24sharon

New member
כן כי הקוד לא הראה את המסגרת כולה

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

בעיקרון זו דרישה לעבודה מול jquery datatables
הדרישות לאוביייקט שחוזר בJSON הם
1. מספר הנתונים הכללי המלא
2. מספר הנתונים המפולטר המלא
3. הנתונים עצמם לפי העמוד , וכן לפי איזה שדה למיין וכן ASC DESC וכן דפדוף
הצעות לשיפור (ללא קוד ממש אלא אפילו הנחיה יספיקו)
ואגב יש את קוד הREPOSITORY הקיים איך אני יודעת אם זה מבצע שאילתת COUNT, או שמבצע שאילתת שליפה + COUNT LIST
תודה
זה הקוד עם הסברים
קוד:
        public JsonResult GetData(ParamsModel param)
        {
//get filter expression from the user 		
            var predicate = Expression(param);
//get full total count
            int totalCount = GetRepositroy<SYS_TBL_People>().Table().Count();
//get filtered data
           var  Peoples = GetRepositroy<SYS_TBL_People>().Table().AsNoTracking()
                .Include(s => s.SYS_TBL_VALS_PeopleStatus) .....    // more includes           
                .AsExpandable().Where(predicate.Compile());
//change the filterd data to model
            var memberCol = Peoples.Select(i => new PeopleModel()
            {
                PeopleId = i.PeopleId,
                FirstName = i.FirstName,
                LastName = i.LastName,
                Subject = i.SYS_TBL_VALS_PeopleSubject != null ? i.SYS_TBL_VALS_PeopleSubject.PeopleSubjectName : "",
		..... // more fields
             }).AsQueryable();
          
            IEnumerable<PeopleModel> filteredMembers = memberCol;
//get the sort field from the user
            int index = param.iSortCol_0;
            Func<PeopleModel, string> orderingFunction = (m => 
                 index == 0
                ? m.PeopleId
                :index == 1
                ? m.Id
         	 ...........);
//get sort type from the user (asc | desc)
            filteredMembers = param.sSortDir_0 == "asc" ? filteredMembers.OrderBy(orderingFunction) : filteredMembers.OrderByDescending(orderingFunction);
//get pagination
            var displayedMembers = filteredMembers
                .Skip(param.iDisplayStart)
                .Take(param.iDisplayLength);
//change results to array
            var result = from a in displayedMembers
                         select new[]
                    {
                        a.PeopleId,
                        a.Id,
                        a.FirstName, 
                      .................. // more fields
                    };

            return Json(new
            {
                sEcho = param.sEcho,
                iTotalRecords = totalCount,
                iTotalDisplayRecords = filteredMembers.Count(),
                aaData = result
            },
                JsonRequestBehavior.AllowGet);
        }

        private Expression<Func<SYS_TBL_People, bool>> Expression(PeoplesParamsModel param)
        {
            var predicate = PredicateBuilder.True<SYS_TBL_People>();
            if (!string.IsNullOrEmpty(param.subjects))
            {

                var subjectsPredicate = PredicateBuilder.False<SYS_TBL_People>();
                string[] subjects = param.subjects.Split(',');

                foreach (string key in subjects)
                {
                    subjectsPredicate = subjectsPredicate.Or(m => m.SubjectId == key);
                }

              
               &nbsp
 
לא הבנתי את התשובה

אמרתי לך דבר עקרוני על דרך העבודה עם EF, למה את מתעלמת?
ראי את הקוד המצורף.

קוד:
public JsonResult GetData(ParamsModel param)
{
    var peoples = GetRepositroy<SYS_TBL_People>()
        .Table(); // Table must implement IQuerable<T>

    //get full total count
    int totalCount = peoples.Count();

    var peoplesQuery = peoples
        // we doesn't need any include, because we runs the query once
        //.Include(s => s.SYS_TBL_VALS_PeopleStatus) // more includes           
        .AsNoTracking();

    if (!string.IsNullOrEmpty(param.subjects))
    {
        string[] subjects = param.subjects.Split(',');
        peoplesQuery = peoplesQuery.Where(x => subjects.Contains(x.Subject));
    }

    if (param.accepted > -1)
    {
        bool accepted = param.accepted == 0;
        peoplesQuery = peoplesQuery.Where(x => (IfAccepted ?? false) == accepted);
    }

    // more filters here

    //get the sort field from the user
    //get sort type from the user (asc | desc)
    int index = param.iSortCol_0;
    bool orderAsc = param.sSortDir_0 == "asc";

    if (index == 0)
    {
        if (orderAsc)
            peoplesQuery = peoplesQuery.OrderBy(x => x.PeopleId);
        else
            peoplesQuery = peoplesQuery.OrderByDesc(x => x.PeopleId);
    }

    if (index == 1)
    {
        if (orderAsc)
            peoplesQuery = peoplesQuery.OrderBy(x => x.Id);
        else
            peoplesQuery = peoplesQuery.OrderByDesc(x => x.Id);
    }

    // select the filterd data to model
    var queryResult = peoplesQuery
        .Skip(param.iDisplayStart)
        .Take(param.iDisplayLength)
        .Select(i => new PeopleModel()
        {
            PeopleId = i.PeopleId,
            FirstName = i.FirstName,
            LastName = i.LastName,
            Subject = i.SYS_TBL_VALS_PeopleSubject?.PeopleSubjectName ?? ""

            // more fields
        }).ToArray();

    var result = new
    {
        sEcho = param.sEcho,
        iTotalRecords = totalCount,
        iTotalDisplayRecords = queryResult.Count(),
        aaData = queryResult
    };

    return Json(result, JsonRequestBehavior.AllowGet);
}
 

24sharon

New member
עברתי על הקוד המצורף

דבר ראשון ולפני הכל תודה רבה ונראה בהחלט שהבנת את הדרישה ב100%
&nbsp
יש כאן פספוס של מספר הרשומות המפולטרות לא כולל הדפדוף, כי כך זה יחזיר תמיד queryResult.Count() משהו כמו 10 רשומות (או מספר הרשומות שהמשתמש בחר)
&nbsp
השינויים שאני רואה בקוד
1. שימוש בcontain במקום or
2. לא מבינה למה לקבל את כל הטבלה שכפי שציינתי אמורה להכיל מליוני רשומות
3. אם מוציאים את הexpression לפונקציה נפרדת למען הסדר הטוב, ואחרי זה מבצעים compile, האם זה לוקח משאבים?
&nbsp
תודה
&nbsp
 
תשובות

בענין הCount את צודקת, החליפי את queryResult.Count בpopleQuery.Count


את צריכה להבין איך עובד Linq.
כאשר את כותבת var query = MyContext.People עדיין לא נעשתה פניה לשרת. המשתנה query איננו מחזיק רשומות אלא אובייקט IQuerable.
גם אם נוסיף var query2 = query.Where.Take.OrderBy.Select עדיין לא פנינו אלא הדטהבייס.
רק כאשר נעשה ToArray או Count או ForEach אז EF מתרגם את הquery לSQL ומחזיר את הרשומות מהדטהבייס.
 

24sharon

New member
אוקיי תודה רבה

אני אבצע לפני ההנחיות אחד לאחד
&nbsp
תודה על ההתייחסות הקונקרטית
 
למעלה