SQL Server Condition in join aggregate function requires value from table that is out of scope

2guxujil  于 2023-03-07  发布在  其他
关注(0)|答案(3)|浏览(161)

Here are the details of the issue...

Table 1: [Student Enrollment]
| STUDENT | COURSE | ENROLL_DT |
| ------------ | ------------ | ------------ |
| 11223 | MATH123 | 01/03/2017 |
| 11223 | AUTO224 | 03/11/2017 |
| 11223 | FABR450 | 09/26/2018 |

Table 2: [Student Track]

STUDENTTRACKDECLARE_DTSTATUS
11223MECH-AAS12/04/2016Active
11223MECH-AAS02/05/2016Discon
11223ENGR-AAS02/20/2017Active

What I'm trying to do:

Get the most recent active declared track at the time of enrollment.
| STUDENT | COURSE | ENROLL_DT | TRACK |
| ------------ | ------------ | ------------ | ------------ |
| 11223 | MATH123 | 01/03/2017 | MECH-AAS |
| 11223 | AUTO224 | 03/11/2017 | ENGR-AAS |
| 11223 | FABR450 | 09/26/2018 | ENGR-AAS |

What I've tried with no avail:

  • Inner join [Student Track] to itself to get max declare date for the student
  • Left join the most recent active track at the time of enrollment to the [Student Enrollment] table

Code:

SELECT  
    STUDENT, COURSE, ENROLL_DT, TRACK
FROM
    [Student Enrollment] AS A
LEFT JOIN 
    (SELECT 
         STUDENT, TRACK, DECLARE_DT
     FROM 
         [Student Track] AS B1
     INNER JOIN 
          (SELECT STUDENT, MAX(DECLARE_DT) as MAX_DECLARE_DT
           FROM [Student Track]
           WHERE DECLARE_DT <= A.ENROLL_DT ***/* ENROLL_DT is out of scope!!!!!!!!!!!!!!!! =( */***
          ) as B2 ON A.STUDENT = B.STUDENT
WHERE 
    STATUS = 'Active'
a0zr77ik

a0zr77ik1#

You can use an APPLY for this, which allows you to access values from the outer scope.

Note that the join condition is moved to inside the subquery, because the subquery is (logically) exceuted once per row of the outer scope.

SELECT  
  se.STUDENT,
  se.COURSE,
  se.ENROLL_DT,
  st.TRACK
FROM
  [Student Enrollment] AS se
OUTER APPLY (
    SELECT TOP (1)
      st.TRACK,
      st.DECLARE_DT
    FROM 
      [Student Track] AS st
    WHERE st.DECLARE_DT <= se.ENROLL_DT
      AND st.STATUS = 'Active'
      AND st.STUDENT = se.STUDENT  -- join condition
    ORDER BY
      st.DECLARE_DT DESC
) AS st;

db<>fiddle

jchrr9hc

jchrr9hc2#

You can use ROW_NUMBER to join the tables.

for this to work, you should have more or equal numbers in enrolment to track

You also should not use spaces in Column or table names, it must be escaped

WITH CTE_track as (SELECT
  [STUDENT],[TRACK],  [DECLARE_DT], ROW_NUMBER() OVER(PARTITION BY STUDENT ORDER BY [DECLARE_DT]) rn
  FROM
[Student Track]),
CTE_enroll as (SELECT
  [STUDENT], [COURSE], [ENROLL_DT], ROW_NUMBER() OVER(PARTITION BY STUDENT ORDER BY [ENROLL_DT]) rn
  FROM
[Student Enrollment])
SELECT
c1.[STUDENT],c1.[COURSE],c1.[ENROLL_DT],c2.[TRACK]
FROM  CTE_enroll c1 LEFT JOIN CTE_track c2  ON c1.[STUDENT] = c2.[STUDENT] AND c1.rn = c2.rn
STUDENTCOURSEENROLL_DTTRACK
11223MATH1232017-01-03 01:00:00.000MECH-AAS
11223AUTO2242017-03-11 01:00:00.000MECH-AAS
11223FABR4502018-09-26 02:00:00.000ENGR-AAS

fiddle

gcxthw6b

gcxthw6b3#

You can use SQL windonw function to get the result you want in the following steps:

  1. Join student_enrollment and student_track table based on student, track active status and enroll_dt should be later than declear_dt.
  2. Use window function for each (student, course) to get most recent track declear_date compared to enroll_date.
  3. Fetch the top row for each (student, course).

Here is the query:

WITH sorted_course AS (
SELECT
    e.student,
    e.course,
    e.enroll_dt,
    t.track,
    t.declear_dt,
    t.status,
    row_number() over (partition by e.student, e.course order by t.declear_dt desc) as row_num
FROM
    student_enrollment e 
JOIN
    student_track t
ON
    e.student = t.student
AND
    t.status = 'Active'
AND
    t.declear_dt <= e.enroll_dt
)
SELECT
    student,
    course,
    enroll_dt,
    track
FROM
    sorted_course
WHERE
    row_num = 1
ORDER BY enroll_dt
studentcourseenroll_dttrack
11223MATH1232017-01-03MECH-AAS
11223AUTO2242017-03-11ENGR-AAS
11223FABR4502018-09-26ENGR-AAS

相关问题