I have a database with, among others, the following two tables:
classes is a straightforward table that has one row per class in a class schedule.
sessions is a table that characterizes the days and times that each class meets, where each row is capable of expressing a notion like:
"Tuesdays | Jan 22-Mar 5 | 6-9pm"
"Tuesdays & Thursdays | Jan 22-Mar 7 | 6-9pm"
"Monday-Thursday | Jan 21-24 | 3-6pm"
"Saturday | Mar 9 | 9am-4pm"
and so on.
There is guaranteed to be at least one row in sessions for each row in classes, and for certain classes there may be two or more associated session rows.
At present, I'm using two different queries to get the class and session information for the classes that match a particular set of criteria, like this:
select c.class_id, c.title, c.instructor, c.num_seats, c.price
from classes c
join classes_by_department cbd
on (cbd.class_id = c.class_id)
join /* several other tables */
on /* several other join conditions */
where cbd.department_id = '{$dept_id}'
and /* several other qualifying conditions */
;
and this:
select s.class_id, s.start_date, s.end_date, s.day_bits, s.start_time, s.end_time
from sessions s
join classes c
on (c.class_id = s.class_id)
join classes_by_department cbd
on (cbd.class_id = s.class_id)
join /* the same other tables */
on /* the same other join conditions */
where cbd.department_id = '{$dept_id}'
and /* the same other qualifying conditions */
;
This works fine, and -- at least in the current application -- the tables aren't big enough, and the traffic isn't heavy enough, for two queries to be a problem. Nevertheless, it strikes me as a bit wasteful, and I'm wondering if there isn't a way to better leverage the work already done by the first query to perform the second one (rather than what amounts to running the same query twice and just selecting different columns).
Of course I realize that I could just select all the relevant columns from classes and sessions in a single query (the second one), but I like the fact that in the current approach, the first query delivers exactly one row per qualifying class, rather than as many rows as the class has session records. I would need to restructure the existing logic that processes the query results if I merged the queries. (Yeah, I know, waah...)
One solution that occurred to me is to collect all the class_ids returned by the first query into a vector (since I have to iterate through those results anyway) and then format the contents of that vector as the content of a value-list for an IN clause, so that the second query would simply become:
select s.class_id, s.start_date, s.end_date, s.day_bits, s.start_time, s.end_time
from sessions s
where s.class_id in (/* value-list */);
I'm not too worried about the scalability of such a solution, as I understand that huge SQL queries are no big deal. Plus, it could take advantage of an index defined over sessions.class_id.
But... well... it's just not very satisfying to someone who's looking to improve his SQL chops, which I'll freely admit are pretty rudimentary. It feels inelegant, and not very "SQL-ish," or whatever the SQL equivalent to the term Pythonic is.
Can anyone suggest something more appropriate?
The canonical way to do what you want is to use views. Define your first query as:
create view vw_MyClasses as
select c.class_id, c.title, c.instructor, c.num_seats, c.price, cbd.department_id
from classes c
join classes_by_department cbd
on (cbd.class_id = c.class_id)
join /* several other tables */
on /* several other join conditions */
where /* several other qualifying conditions */
Then your class query would be:
select *
from vw_MyClasses
where department_id = '{$dept_id}'
Then, your second query can be:
select s.class_id, s.start_date, s.end_date, s.day_bits, s.start_time, s.end_time
from sessions s
where s.class_id in (select class_id from vw_MyClasses
where department_id = '{$dept_id}');
Or, what may be more efficient in MySQL:
select s.class_id, s.start_date, s.end_date, s.day_bits, s.start_time, s.end_time
from sessions s
where exists (select 1 from vw_MyClasses mc where mc.class_id = s.class_id limit 1)
There is a very good reason for doing this. Repeating such logic in multiple queries becomes a maintenance nightmare. As you modify the logic in one place, it is very easy to forget to make the modifications in all places. Sometimes, views are not sufficient, so you may need to use user defined functions, as explained here.
Also, if the criteria are so useful, you might want to put flags in the class table to identify them. This requires maintaining them in some way, such as nightly updates or using triggers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With