简体   繁体   中英

C# Linq to SQL, find Min and Max dates in grouped rows

I have a large school district database, from which I need to determine the start and end dates for terms, semesters, and full years. Each school could have different holidays (which along with weekends calculate into the term start date), though it's not likely. I have gotten as far as pulling the schools, their holidays, and the END date of each term and from that calculated the beginning date of each term. So far so good. But some schools have some classes registered in terms and other classes that are full year (September to June). So I want my final returned date to look something like (this represents one of many schools):

SchoolId SchoolYear TermCode    TermBegin        TermEnd      TermCodeId
8CFD1     2016      TM1         08/29/2016       10/07/2016     F375E7
8CFD1     2016      TM2         10/10/2016       11/25/2016     898EF5
8CFD1     2016      TM3         11/28/2016       02/01/2017     F5140C
8CFD1     2016      TM4         02/02/2017       03/10/2017     DD95B1
8CFD1     2016      TM5         03/13/2017       05/05/2017     BE3635
8CFD1     2016      TM6         05/08/2017       06/09/2017     2B94F0
8CFD1     2016      YR          08/29/2016       06/09/2017     D97C9C

The closest I have gotten is this query (using results where I have calculated the start date):

from tdf in ReturnFromCalculatingStartDate 
            join tcd in SCH_YR_TRM_CODES on tdf.SchoolYearTermDefGu equals tcd.SCHOOL_YEAR_TRM_DEF_GU
            orderby tcd.TERM_CODE
            select new TermResult {     
                SchoolId = testOrgGu,
                SchoolYear = testSchoolyear,
                TermCode = tcd.TERM_CODE,
                TermBegin = tdf.TermStartDate,
                TermEnd = tdf.TermEndtDate,
                TermCodeId = tcd.SCHOOL_YEAR_TRM_CODES_GU

Which returns the terms correctly, but I get a year (YR) for each term. What I need is one YR row that has the Min(TermBegin) and the Max(TermEnd) of the YR rows. I would need to group on the SchoolId and TermCode and get the Min and Max of each unique code, but I don't know how.

The data I get back from the above Linq is below. The TermCodeId from the Min(TermBegin) YR row is the TermCodeId I need to have in my results.

SchoolId    SchoolYear  TermCode  TermBegin    TermEnd     TermCodeId
   8CFD1    2016         TM1        08/29/2016  10/07/2016  F375E7
   8CFD1    2016         TM2        10/10/2016  11/25/2016  898EF5
   8CFD1    2016         TM3        11/28/2016  02/01/2017  F5140C
   8CFD1    2016         TM4        02/02/2017  03/10/2017  DD95B1
   8CFD1    2016         TM5        03/13/2017  05/05/2017  BE3635
   8CFD1    2016         TM6        05/08/2017  06/09/2017  2B94F0
   8CFD1    2016         YR         08/29/2016  10/07/2016  d97C9C
   8CFD1    2016         YR         10/10/2016  11/25/2016  e30980
   8CFD1    2016         YR         11/28/2016  02/01/2017  ef5de2
   8CFD1    2016         YR         02/02/2017  03/10/2017  bbe78f
   8CFD1    2016         YR         03/13/2017  05/05/2017  ca28a7
   8CFD1    2016         YR         05/08/2017  06/09/2017  2340c1

Appreciate the help, getting this far was challenging.

Here is how to tackle this issue:

  1. Get all the records where the TermCode is NOT YR
  2. Get all the records where the TermCode is YR but group them by SchoolId , SchoolYear , TermCode and TermCodeId and get the Max(TermBegin) and Max(TermEnd) for each group.
  3. Perform a Union on results in step 1 and 2 above.

Step 1

All we need to is to add a where clause to your query like this:

where tcd.TERM_CODE != "YR"

But lets save the query in a variable so it is more readable. Please note the addition of the where clause:

var excludingYr =
    from tdf in ReturnFromCalculatingStartDate
    join tcd in SCH_YR_TRM_CODES on tdf.SchoolYearTermDefGu
        equals tcd.SCHOOL_YEAR_TRM_DEF_GU
    where tcd.TERM_CODE != "YR"
    orderby tcd.TERM_CODE
    select new TermResult
    {
        SchoolId = testOrgGu,
        SchoolYear = testSchoolyear,
        TermCode = tcd.TERM_CODE,
        TermBegin = tdf.TermStartDate,
        TermEnd = tdf.TermEndtDate,
        TermCodeId = tcd.SCHOOL_YEAR_TRM_CODES_GU
    };

Step 2

We need to add a where clause and a group by and Min and 'Max':

var yearRecords =
    from tdf in ReturnFromCalculatingStartDate
    join tcd in SCH_YR_TRM_CODES on tdf.SchoolYearTermDefGu
        equals tcd.SCHOOL_YEAR_TRM_DEF_GU
    where tcd.TERM_CODE == "YR"
    orderby tcd.TERM_CODE
    group tdf by new
        {
            SchoolId = testOrgGu,
            SchoolYear = testSchoolyear,
            TermCode = tcd.TERM_CODE,
            TermCodeId = tcd.SCHOOL_YEAR_TRM_CODES_GU
    } into yrs
    select new TermResult
    {
        SchoolId = yrs.Key.SchoolId,
        SchoolYear = yrs.Key.testSchoolyear,
        TermCode = yrs.Key.TermCode,
        TermBegin = yrs.Min(x => x.TermBegin),
        TermEnd = yrs.Max(x => x.TermBegin),
        TermCodeId = yrs.Key.TermCodeId
    };

Step 3

Let's perform a Union :

var finalQuery = excludingYr.Union(yearRecords);

Because you need the TermCodeId , the 2nd step will have duplicate records so we need to do some gymnastics here to remove duplications. See this answer to see how you can do that part.

Please note I could not compile or test the above so you may need to tweak it but the steps are all there and since you have the data, it should not be easy to do.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM