Module Output
**SQL: [IDI_Community].[edu_school_enrolment_status].[school_enrolment_status_YYYYMM]
**SAS: libname cm_dlr_ODBC dsn=idi_community_srvprd schema=edu_school_enrolment_status; proc print data = cm_dlr.school_enrolment_status_YYYYMM ;run ;
How to access a code module in the Data Lab:Read here
Purpose
This code creates spells of school enrolment for ākonga enrolled in school in New Zealand. There are two output tables associated with this module corresponding to two use cases:
- a table containing information about which school each student is enrolled in (enrolment spells)
- a table containing information about whether a student is enrolled in any school (enrolment status)
This script is responsible for the second table.
Key Concepts
ENROL vs School Roll Return data sources
ENROL is a register of student enrolments. Schools are legally required to update enrolment records in ENROL within 5 school days as students enrol, change schools or leave the school system. The source of the student rolls in the school directory come from ENROL. This is updated on a monthly basis (during the school year). ENROL is also the source data for this module.
School Roll Return (SRR) is another source of roll information. There are four school roll returns every year. All schools complete a snapshot of roll information as at 1 March and 1 July. The Ministry of Education also collects 1 June and 1 September rolls from state and state-integrated schools with students in funding year levels 9 to 15. Charter Schools also complete the 1 June and 1 September returns. Most schools use a student management system (SMS) to manage their student records, and to complete the roll returns for the Ministry. SRR data is reported on the MoE education counts public facing page.
Enrolments vs Attendance
If a student is attending a school, then the school has a legal requirement to enrol that student in ENROL. Please see this public facing Ministry page.
A child can start school when they turn 5, but they are not legally required to start until they turn 6. Once they are enrolled in school they are legally required to attend until they are 16. If a student does not attend school while they are enrolled, this is dealt with based on the school’s internal policies. Once the student has been absent for 20 consecutive school days (unless they have been told that the student’s absence is only temporary) the school should then withdraw them with their last date of attendance at the school. Please see this public facing Ministry page
There are a small number of situations where a student can be enrolled at multiple schools simultaneously. These include dual enrolments with Te Kura, or off-site enrolments with Health Schools or Alternative Education Providers.
Comparison against other sources
At a given snapshot of time, the enrolment spells module can be used to re-create school roll counts. Snapshot roll counts can be summarised from the table [IDI_Clean_].[moe_clean].[school_roll_return] at two different dates every year: March 1st and July 1st. Mismatches can be investigated at the student level.
Public facing school roll summaries, derived from the snapshot School Roll Return, are also available from MoE’s EdCounts page. These public facing summaries do not always match those obtained from the [moe_clean].[school_roll_return] table.
The module tables are derived from the source table [IDI_Clean_].[moe_clean].[student_enrol] through a cleaning process. A comparison to this source can be used to understand the extent of the cleaning process.
References & Contacts
- James Swindells (MoE)
- Cheryl Remington (MoE)
- Angus Prain (Stats)
Development team
| Domain | Agency | Person |
|---|---|---|
| Lead SME | SIA | Andrew Webber |
| Lead SME | UC | Peng Sun |
| IDI analysts | Stats | Angus Prain |
| Data Supply | MoE | ENROL |
| Policy/Operational Experts | MoE | Chelsea Shaw |
Module Business Rules
The [enrolment_status] output table consists of unique combinations of (student, spell start, spell end, enrolment status) (which are snz_uid, startdate, enddate, spell_type as column names in the table). The table is unique on student, startdate and enddate, and spells are never overlaping.
The script uses the following business rules:
- All business rules that apply to the enrolment_spells table apply
- Starting from the enrolment_spells table, non-overlapping spells of enrolment status (enrolled or not-enrolled) are constructed
Open Issues/Comments
- All issues indicated in the
[enrolment_spells]script header apply.
Parameters
The following parameters should be supplied to this module to run it in the database:
{targetdb}: The SQL database on which the spell datasets are to be created.{idicleanversion}: The IDI Clean version that the spell datasets need to be based on.{targetschema}: The project schema under the target database into which the spell datasets are to be created.{projprefix}: A (short) prefix that enables you to identify the spell dataset easily in the schema, and prevent overwriting any existing datasets that have the same name.{max_attendance_date}: the max date in student_attendance table
Dependencies
[school_enrolment_spells]code module[data].[personal_detail]
Outputs
{targetdb}.{targetschema}.{projprefix}_school_enrolment_status
Variable Descriptions
| Column name | Description |
|---|---|
| snz_uid | Stats nz unique identifier |
| startdate | Spell start date |
| enddate | Spell end date |
| spell_days | Length of spell in days |
| start_level | School level of the student at spell start date |
| spell_end_reason | Description of why the spell ended - this derived from SpellEndReason in the [student_enrol] table |
| age_at_startdate | Age of student at start date of spell |
| age_at_enddate | Age of student at end date of spell |
Module Version & Change History
| Date | Version | Comments |
|---|---|---|
| 4 June 2025 | Initial Version based on specifications from Commissioning document. |
Code
:setvar targetdb "{targetdb}"
:setvar targetschema "{targetschema}"
:setvar projprefix "{projprefix}"
:setvar idicleanversion "{idicleanversion}"
:setvar idimetaversion "{idimetaversion}"
:setvar max_attendance_date "{max_attendance_date}"
/*
:::::::::::::::::::::
0. GLOBAL SETUP
:::::::::::::::::::::
*/
DROP TABLE IF EXISTS #t1_complete_spells;
SELECT *
, ROW_NUMBER() OVER(PARTITION BY snz_uid ORDER BY startdate) AS rn
INTO #t1_complete_spells
FROM $(targetdb).$(targetschema).$(projprefix)_school_enrolment_spells;
GO
PRINT('school_enrolment_spells TABLE imported to scope');
/*
BEGIN check
select * from #complete_spells order by snz_uid, startdate, enddate
END check
*/
/*
::::::::::::::::::::::::::::::::::::::::::
6. Generate the derived 'enrolment_status' table
::::::::::::::::::::::::::::::::::::::::::
This table is only enrolment statud, as per the commissioning document
*/
DROP TABLE IF EXISTS #clean_spells; ;
WITH long_dates AS(
SELECT snz_uid
, startdate AS date_value
, 'startdate' AS date_type
, CASE WHEN unique_spell_reason = 'enrolment_gap' THEN 'not_enrolled' ELSE 'enrolled' END AS spell_type
, rn
, sch_enddate_imputed
, spell_end_reason
, start_level
FROM #t1_complete_spells
UNION ALL
SELECT snz_uid
, enddate AS date_value
, 'enddate' AS date_type
, CASE WHEN unique_spell_reason = 'enrolment_gap' THEN 'not_enrolled' ELSE 'enrolled' END AS spell_type
, rn
, sch_enddate_imputed
, spell_end_reason
, start_level
FROM #t1_complete_spells
)
, pre_spells AS(
SELECT snz_uid
, date_value
, date_type
, spell_type
, rn
, start_level
, spell_end_reason
, sch_enddate_imputed
, MAX(rn) OVER(PARTITION BY snz_uid) AS max_rn
, CASE WHEN rn = MAX(rn) OVER(PARTITION BY snz_uid) AND sch_enddate_imputed <> 1
AND date_type = 'enddate' THEN 'END' WHEN LEAD(spell_type) OVER(PARTITION BY snz_uid ORDER BY rn, date_value) <> spell_type THEN 'END' WHEN rn = 1
AND date_type = 'startdate' THEN 'start' WHEN LAG(spell_type) OVER(PARTITION BY snz_uid ORDER BY rn, date_value) <> spell_type
AND rn <> 1 THEN 'start' WHEN sch_enddate_imputed = 1
AND date_type = 'enddate' THEN 'extraction_date' END AS boundary
FROM long_dates
)
, long_spells AS(
SELECT *
, CASE WHEN LEAD(rn) OVER(PARTITION BY snz_uid ORDER BY rn) >= rn THEN LEAD(date_value) OVER(PARTITION BY snz_uid ORDER BY rn) END AS enddate
, CASE WHEN LEAD(rn) OVER(PARTITION BY snz_uid ORDER BY rn) >= rn THEN LEAD(spell_end_reason) OVER(PARTITION BY snz_uid ORDER BY rn) END AS reason_for_leaving_next
, CASE WHEN LEAD(boundary) OVER(PARTITION BY snz_uid ORDER BY rn) = 'extraction_date' THEN 'extraction_date' END AS is_extraction_date
FROM pre_spells
WHERE boundary IS NOT NULL
)
SELECT a.snz_uid
, spell_type
, DATEDIFF(d, date_value, enddate) AS spell_length
, date_value AS startdate
, enddate
, start_level
, CASE WHEN is_extraction_date IS NOT NULL THEN 'Enddate Imputed'
WHEN spell_type = 'not_enrolled' THEN 'New Enrolment'
ELSE reason_for_leaving_next END AS spell_end_reason
INTO #clean_spells
FROM long_spells a
WHERE date_type = 'startdate';
GO
PRINT('#clean_spells TABLE generated');
/* adding age at start and end of spell */
DROP TABLE IF EXISTS #clean_spells_with_age;
SELECT a.*
, DATEDIFF(YEAR, snz_birth_date_proxy, startdate) AS age_at_startdate
, DATEDIFF(YEAR, snz_birth_date_proxy, enddate) AS age_at_enddate
INTO #clean_spells_with_age
FROM #clean_spells a
JOIN $(idicleanversion).[data].[personal_detail] b
ON a.snz_uid = b.snz_uid ;
GO
PRINT('#clean_spells_with_age TABLE generated');
DROP TABLE IF EXISTS $(targetdb).$(targetschema).$(projprefix)_school_enrolment_status;
SELECT *
INTO $(targetdb).$(targetschema).$(projprefix)_school_enrolment_status
FROM #clean_spells_with_age;