for >99% of students, their year group will change every September (going up +1).
That suggests an alternative approach: instead of storing "year group", it may be better to store a "cohort identifier" (e.g. nominal intake year, or a September-based birth year) and map that to the year group. In September, the mapping base changes, ...
"Generic database" a.k.a. column-oriented
This is a comment, not an answer.
A cursory read of column-oriented sounds like what we called a generic DB. We built and maintained a generic database using MS/SQL. Note: I don't recall that performance was a particular issue...
Verbose queries - even the simplest, practical useful queries needed 2 or ...
Is it expected that an object's isCompound could change after it's constructed? I think that member ought to be const - or better still, use an intermediate class:
class CompoundDataType : public BaseDataType
I don't see where this value is ever used, so perhaps it could be removed entirely?
The empty destructor can be explicitly defaulted:
Here is one way to avoid hitting table 2 twice:
select tmp.* from (
select id, phone1 as phone_num, date from table_1
select id, phone2 as phone_num, date from table_1
left join table_2 t2 on phone_num in (t2.phone1, t2.phone2)
where t2.id is null