Link to home
Create AccountLog in
Avatar of lisa_mc
lisa_mcFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Only output last record from a cte

Hi experts

I am currently working on alot of attendance reports

The code below allows the user to enter two value say for example 5 and 9.  This will then show all the students who have missed between 5 and 9 consecutive days off their course (student has been absent for all classes each day).  The code also shows which classes (subjects) that the students have missed for each day so if you have 15,000 students quite alot of information is outputted.

What I would like to do is a summary report like for each student only have the last class they attended and what date that occured and how many days absent and possibly absent start and absent end.

Basically I need to get the last record for each student and only output this to the user

Thanks
--drop table #absencerep
if @pAcadPeriod is null 
begin
   select @pAcadPeriod=MAX(acad_period) 
      from  nicisreports..nrc_eRegisters_allstudents_tb as x   --yourtable
               --  student_REgister as x
end

select student_id,DATE as regdate,CONVERT(integer,0) as numless,CONVERT(integer,0) as absentless
         ,CONVERT(integer,0) as present
         into #absencerep
         from nicisreports..nrc_eRegisters_allstudents_tb as x   --yourtable
                 --student_REgister as x
        where 0 = 1  
		


       
 create clustered index xxx on #absencerep (student_id,regdate,present)

 -- identify students attendance per day
     Insert into #absencerep
         select student_id,[date] as regdate
              ,count(*) as numless
              ,SUM(case absence_code when 'o' then 1 else 0 end) as absentless
              ,SUM(case absence_code when 'o' then 0 else 1 end) as present
            
            from nicisreports..nrc_eRegisters_allstudents_tb as x   --yourtable
                 --student_REgister as x
           where acad_period=@pAcadPeriod
           group by student_id,[date]
           order by 1,2,present
  select @err=@@ERROR,@rows=@@ROWCOUNT
  if @err<>0 
  begin
     --
     set @rc=1
     return @rc
  end

--select * from #absencerep


  
  ;with      
  cte1a as (select * from #absencerep where present=0)  -- absent student days

        --select * from cte1a


,cte2MAX as -- GET MAX ENDDATE FOR A START DATE
       (select a.student_id,a.regdate as Abstart
                  ,MAX(b.regdate) as Abend
                  ,sum(b.numless) as Lessoncount,COUNT(*) as AbsentDays
                  ,SUM(b.absentless) as Absents
                     
          from CTE1a as a   
          Inner Join cte1a as B
            on a.student_id=b.student_id
           and b.RegDate>=a.RegDatE
     where not exists (select student_id from #absencerep as x
                        where a.student_id=x.student_id
                         AND X.RegDate BETWEEN A.RegDate AND B.RegDate
                         AND X.present>0
                      )
      group by a.student_id,a.regdate
     HAVING COUNT(*) >= @minabsence  
     -->4    -- >4 absence of "5 or more days"   
      ) 

--select * from cte2MAX

 
 ,CTE2 AS -- GET THE Abscence PERIOD RANGE FOR A STUDENT
       (SELECT STUDENT_ID,ABstart,abend,lessoncount,absentdays,absents
          FROM (SELECT X.*
                     ,ROW_NUMBER() OVER (PARTITION BY STUDENT_ID,ABEND ORDER BY ABSTART ) AS RN
                  FROM cte2MAX as x
                ) as x
         where RN=1
          and AbsentDays <= @maxabsence
       )
    -- select * from cte2 ORDER BY 1,2


 --  students with absence periods of 5 or more days

	insert into nrc_INF0149_5consec_daysoff_tb
 
   select x.*, AbsentDays, Abstart, Abend, Absents
     from nicisreports..nrc_eRegisters_allstudents_tb as x   --yourtable
                -- student_REgister as x
    Inner Join cte2 as a
      on x.student_id=a.student_id
     and x.[date] between a.Abstart and a.Abend
	where stage_code = 'ENR'
	and moa in ('01', '02', '20')
    order by x.student_id,x.[date],x.[start_time]

Open in new window

Avatar of ericpeckham
ericpeckham
Flag of United States of America image

The usual practice for getting the last record of anything is...

 
SELECT TOP (1) ...
FROM ...
WHERE ...
ORDER BY <column_name> DESC

Open in new window

Avatar of lisa_mc

ASKER

hi eric so how would I apply it to the above code
Avatar of lisa_mc

ASKER

i put top 1 in on line 90 and it will only give me first record of all students but I need last recrod for each student
Don't have time to study your whole query, but I think the following should do the trick (I have slightly modified your final select):

 
select top(1) x.*, AbsentDays, Abstart, Abend, Absents
     from nicisreports..nrc_eRegisters_allstudents_tb as x   --yourtable
                -- student_REgister as x
    Inner Join cte2 as a
      on x.student_id=a.student_id
     and x.[date] between a.Abstart and a.Abend
	where stage_code = 'ENR'
	and moa in ('01', '02', '20')
    order by x.student_id,x.[date],x.[start_time] desc

Open in new window


Note the desc to order the start date in descending order - I presume that will give you the last record.
Avatar of lisa_mc

ASKER

hi again eric

again thats only pulling out one student record out of my whole dataset

but I need one record for each student
Oh, wait - you want the last record for each student, not for all students.  Duh! :-)  Solution coming...
Avatar of lisa_mc

ASKER

yeah thats right :-)

I need the last class that the student attended
Ok - trying to save my reputation after that really dumb answer I already gave - I've modified your code - my additions have been commented with my initials EP:

 
--drop table #absencerep
if @pAcadPeriod is null 
begin
   select @pAcadPeriod=MAX(acad_period) 
      from  nicisreports..nrc_eRegisters_allstudents_tb as x   --yourtable
               --  student_REgister as x
end

select student_id,DATE as regdate,CONVERT(integer,0) as numless,CONVERT(integer,0) as absentless
         ,CONVERT(integer,0) as present
         into #absencerep
         from nicisreports..nrc_eRegisters_allstudents_tb as x   --yourtable
                 --student_REgister as x
        where 0 = 1  
		


       
 create clustered index xxx on #absencerep (student_id,regdate,present)

 -- identify students attendance per day
     Insert into #absencerep
         select student_id,[date] as regdate
              ,count(*) as numless
              ,SUM(case absence_code when 'o' then 1 else 0 end) as absentless
              ,SUM(case absence_code when 'o' then 0 else 1 end) as present
            
            from nicisreports..nrc_eRegisters_allstudents_tb as x   --yourtable
                 --student_REgister as x
           where acad_period=@pAcadPeriod
           group by student_id,[date]
           order by 1,2,present
  select @err=@@ERROR,@rows=@@ROWCOUNT
  if @err<>0 
  begin
     --
     set @rc=1
     return @rc
  end

--select * from #absencerep


  
  ;with      
  cte1a as (select * from #absencerep where present=0)  -- absent student days

        --select * from cte1a


,cte2MAX as -- GET MAX ENDDATE FOR A START DATE
       (select a.student_id,a.regdate as Abstart
                  ,MAX(b.regdate) as Abend
                  ,sum(b.numless) as Lessoncount,COUNT(*) as AbsentDays
                  ,SUM(b.absentless) as Absents
                     
          from CTE1a as a   
          Inner Join cte1a as B
            on a.student_id=b.student_id
           and b.RegDate>=a.RegDatE
     where not exists (select student_id from #absencerep as x
                        where a.student_id=x.student_id
                         AND X.RegDate BETWEEN A.RegDate AND B.RegDate
                         AND X.present>0
                      )
      group by a.student_id,a.regdate
     HAVING COUNT(*) >= @minabsence  
     -->4    -- >4 absence of "5 or more days"   
      ) 

--select * from cte2MAX

 
 ,CTE2 AS -- GET THE Abscence PERIOD RANGE FOR A STUDENT
       (SELECT STUDENT_ID,ABstart,abend,lessoncount,absentdays,absents
          FROM (SELECT X.*
                     ,ROW_NUMBER() OVER (PARTITION BY STUDENT_ID,ABEND ORDER BY ABSTART ) AS RN
                  FROM cte2MAX as x
                ) as x
         where RN=1
          and AbsentDays <= @maxabsence
       )
    -- select * from cte2 ORDER BY 1,2
-- EP added following CTE    
,CTE3 AS
(
	SELECT Student_ID, MAX(ABstart) AS ABstart, MAX(abend) AS ABend
	FROM CTE2
	GROUP BY Student_ID, lessoncount, absentdays, absents
)

 --  students with absence periods of 5 or more days

	insert into nrc_INF0149_5consec_daysoff_tb
 
   select x.*, AbsentDays, Abstart, Abend, Absents
     from nicisreports..nrc_eRegisters_allstudents_tb as x   --yourtable
                -- student_REgister as x
    Inner Join cte2 as a
      on x.student_id=a.student_id
     and x.[date] between a.Abstart and a.Abend
-- EP added following join     
    Inner Join cte3 as a1 on x.student_id = a1.student_id and a.ABstart = a1.ABstart
	where stage_code = 'ENR'
	and moa in ('01', '02', '20')
    order by x.student_id,x.[date],x.[start_time]

Open in new window


The cte (cte3) I added should get only one record for each student, and then we join to that in the final select, which should also result in only one record for each student.  I'm sorry, as I don't have your base tables, I couldn't test the code, but even if there's an error in it, I hope you get the idea and that with minor adjustments it will work for you.  Good luck!
Avatar of lisa_mc

ASKER

hi again

Im getting two errors

Msg 209, Level 16, State 1, Line 34
Ambiguous column name 'Abstart'.
Msg 209, Level 16, State 1, Line 34
Ambiguous column name 'Abend'.
Ah, of course - you need to now prefix them as follows, because of the addition join with the same column names:

 
select x.*, AbsentDays, a1.Abstart, a1.Abend, Absents
     from nicisreports..nrc_eRegisters_allstudents_tb as x   --yourtable
                -- student_REgister as x
    Inner Join cte2 as a
      on x.student_id=a.student_id
     and x.[date] between a.Abstart and a.Abend
-- EP added following join     
    Inner Join cte3 as a1 on x.student_id = a1.student_id and a.ABstart = a1.ABstart
	where stage_code = 'ENR'
	and moa in ('01', '02', '20')
    order by x.student_id,x.[date],x.[start_time]

Open in new window

Avatar of lisa_mc

ASKER

sorry to say thats not giving me one record for example I have a student appearing 15 times

from what I can see all of the students have more than one record
Hmm, your complex query is going to need more study and time, then, which I'm afraid I don't have at the moment.  I'll come back to this later on, if someone hasn't given you the solution before then.  I was hoping you would get some ideas from what I was doing, and then apply them correctly to your situation.  There must be another statement that is returning multiple records with the same dates - if the multiple records you're seeing for each student are identical, do SELECT DISTINCT in your final select statement.  Otherwise, I hope either you or someone else will be able to find you a solution.
Avatar of lisa_mc

ASKER

yeah there are records for the same dates as a student can have mnore than one class per day

some students can have 1 class some can have 4 classes per day - this depends on the course
ASKER CERTIFIED SOLUTION
Avatar of Mark Wills
Mark Wills
Flag of Australia image

Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
See answer
Avatar of lisa_mc

ASKER

perfect solution

thanks again