Ik heb twee tabellen in MySQL:
Korte toelichting:
1. N.a.v. een advertentie wordt een mail verstuurd. De verzenddatum wordt opgeslagen in de tabel 'mails'
2.. Velden die niet relevant zijn heb ik voor het gemak weggelaten. De tabellen zijn in werkelijkheid veel uitgebreider.
Anders gezegd:
Een advertisement has many mails
EN
Een mail belongs to an advertisement
Ik wil alle advertenties tevoorschijn halen waarvan in de afgelopen X tijdsperiode geen mails verstuurd zijn.
Ik kom tot de volgende query:
Toelichting:
1. '#{n.days.ago}' is Ruby-code die een tijdsstempel genereert die MySQL snapt
Bovenstaande query doet wat ik wil, maar is natuurlijk best 'zwaar'. Ik heb er lang over nagedacht, maar kan niet tot een oplossing komen die efficienter is.
Mijn probleem is dat mijn kennis van SQL enigzins tekort schiet. Hierdoor ben ik bang dat ik uitindelijk een query bouw die bij tests lijkt te werken (door toeval), maar in werkelijkheid niet precies doet wat ik wil.
Van bovenstaande query ben ik er van overtuigd dat die werkt, omdat ik de query volledig 'doorzie'.
Ik zat zelf te denken in de volgende richting, maar ik kom niet tot de goede oplossing:
- Een INNER JOIN die alleen de eerste rij ophaalt van mails gesorteerd op send_time aflopend en dan vervolgens een WHERE clausule 'send_time < #{n.days.ago}'. Kan ik echter wel een CLAUSULE op een INNER JOIN zetten?
Denk ik in de goede richting? Wie kan me helpen tot een efficientere oplossing te komen, zonder dat ik een query bouw die 'bij toeval werkt', maar in werkelijkheid niet doe wat ik wil.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| mails +------------------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------------+----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | send_time | datetime | YES | | NULL | | | advertisement_id | int(11) | YES | MUL | 0 | | +------------------+----------+------+-----+---------+----------------+ advertisements +---------------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | +---------------------+--------------+------+-----+---------+----------------+ |
Korte toelichting:
1. N.a.v. een advertentie wordt een mail verstuurd. De verzenddatum wordt opgeslagen in de tabel 'mails'
2.. Velden die niet relevant zijn heb ik voor het gemak weggelaten. De tabellen zijn in werkelijkheid veel uitgebreider.
Anders gezegd:
Een advertisement has many mails
EN
Een mail belongs to an advertisement
Ik wil alle advertenties tevoorschijn halen waarvan in de afgelopen X tijdsperiode geen mails verstuurd zijn.
Ik kom tot de volgende query:
code:
1
2
3
4
5
6
7
8
9
| SELECT `advertisements`.* FROM `advertisements` WHERE (id NOT IN ( SELECT DISTINCT(advertisement_id) FROM mails WHERE send_time > '#{n.days.ago}' ) ) |
Toelichting:
1. '#{n.days.ago}' is Ruby-code die een tijdsstempel genereert die MySQL snapt
Bovenstaande query doet wat ik wil, maar is natuurlijk best 'zwaar'. Ik heb er lang over nagedacht, maar kan niet tot een oplossing komen die efficienter is.
Mijn probleem is dat mijn kennis van SQL enigzins tekort schiet. Hierdoor ben ik bang dat ik uitindelijk een query bouw die bij tests lijkt te werken (door toeval), maar in werkelijkheid niet precies doet wat ik wil.
Van bovenstaande query ben ik er van overtuigd dat die werkt, omdat ik de query volledig 'doorzie'.
Ik zat zelf te denken in de volgende richting, maar ik kom niet tot de goede oplossing:
- Een INNER JOIN die alleen de eerste rij ophaalt van mails gesorteerd op send_time aflopend en dan vervolgens een WHERE clausule 'send_time < #{n.days.ago}'. Kan ik echter wel een CLAUSULE op een INNER JOIN zetten?
Denk ik in de goede richting? Wie kan me helpen tot een efficientere oplossing te komen, zonder dat ik een query bouw die 'bij toeval werkt', maar in werkelijkheid niet doe wat ik wil.