Het is lang geleden dat ik op dit forum actief geweest ben. Hopelijk kan iemand me helpen met het volgende.
Inleiding
Voor mijn muziekvereniging heb ik software ontwikkeld waarin we nagenoeg alle relevante zaken registreren. Daar hoort ook een administratie van instrumenten bij.
Er wordt bijgehouden welke instrumenten er worden onderhouden en welk type onderhoud het betreft: periodiek onderhoud of incidenteel onderhoud. Het idee van periodiek onderhoud is dat - zeker bepaalde typen instrumenten - hoe dan ook om de zoveel tijd (preventief) onderhoud nodig hebben.
Voor het periodieke onderhoud is een (eenvoudig) plan opgevat. Dit komt er in de kern op neer dat voor instrumenten een onderhoudsfrequentie is bedacht en dat vervolgens het aantal instrumenten over die onderhoudsperiode wordt verdeeld. Dus stel dat we 18 klarinetten hebben en de standaard onderhoudsfrequentie is eens per 3 jaar, dan zijn ieder jaar dus 6 klarinetten aan de beurt.
Voor de helderheid: het is geen optie om alle instrumenten tegelijkertijd te laten onderhouden, omdat je dan alle muzikanten wekenlang hun instrument ontneemt. :-). Juist het roulatiesysteem maakt dat er waar nodig tijdelijk een leeninstrument of een instrument op voorraad kan worden gebruikt.
Op dit moment staat nog op papier wanneer welk instrument aan de beurt is. Dat is natuurlijk heel bewerkelijk. En zonde; de administratie is immers in de basis al digitaal.
Het doel
Dat moet slimmer kunnen, dacht ik. Het doel is simpel: de computer moet in feite 'met een druk op de knop' een lijst instrumenten presenteren die nu aan de beurt zijn voor periodiek onderhoud. En ik kom een heel eind, maar ik loop toch enigszins vast. Mijn voorgevoel zegt dat een frisse blik van jullie wonderen gaat doen.
Huidige tabelstructuur
De onderliggende tabelstructuur is - voor zover relevant - op dit moment als volgt. Ik doe het maar even in de vorm van query's, mochten jullie het liever anders zien dan hoor ik graag.
Een tabel instrumentenlijst.
Deze tabel bevat de kerngegevens van alle instrumenten.
Een tabel instrumenttypes
Een tabel instrumenten_onderhoudshistorie.
Deze tabel bevat op regelniveau details over uitgevoerde reparaties, hetzij periodiek, hetzij incidenteel.
En de tabel instrumenten_onderhoud_standaardfrequentie.
In deze tabel wordt per instrumenttype de standaard onderhoudsfrequentie ingevoerd (bijv. klarinetten per 36 maanden, trombones per 96 maanden). Ook wordt de het aantal rondes per frequentie ingevoerd. Dus bij een termijn van 36 maanden en 3 rondes per keer, vindt er effectief jaarlijks onderhoud plaats op 1/3 van de instrumenten van dat type.
Mijn huidige oplossing
Mijn huidige oplossing komt er op neer dat ik een query heb opgezet waarbij
Het idee is dat de PHP-code
Vraag 1: kan dit niet eenvoudiger?
Deze oplossing levert een gigantisch lange query op. Mijn voorgevoel zegt dat dit ongetwijfeld korter en slimmer moet kunnen. Heeft iemand daarvoor wellicht de gouden tip?
De UNION heb ik nodig omdat ik in de WHERE filter op instrumenttype. Dat heb ik echter nodig, omdat ik anders ofwel
Vraag 2: hoe ga ik slimmer om met dynamisch wijzigende gegevens?
Een ander punt is dat deze query - naast de omvang - een ander groot nadeel heeft: het dynamische effect ervan. Daarmee doel ik op het effect dat de query niet corrigeert voor al in deze periode uitgevoerd periodiek onderhoud.
Dus als ik vandaag een lijst opvraag van instrumenten die "aan de beurt" zijn, deze instrumenten laat onderhouden en dat onderhoud invoeren, dan zijn na die invoer volgens het systeem ogenblikkelijk alweer de volgende instrumenten aan de beurt. Terwijl ik daarvoor nu juist die standaard verdeling van onderhoudsfrequenties voor had bedacht.
Ik heb dit laatste proberen te corrigeren met een WHERE-voorwaarde die detecteert of er in de periode tussen nu en de onderhoudsperiode per subgroep (dus 12 maanden in het voorbeeld) periodiek onderhoud is geweest. Dan wordt echter geen enkel instrument meer getoond zodra er 1 instrument voor periodiek onderhoud is ingevoerd. Dat is niet handig; het moet niet zo zijn dat als de instrumentenbeheerder een dag later nog even wil dubbelchecken welke instrumenten ook alweer aan de beurt zijn dat dan de lijst alweer op 0 staat. Of geforceerd wordt van lijstjes printjes te maken omdat hij weet dat zodra er 1 onderhoud wordt ingevoerd de lijst weg is. Ik wilde juist van het papier af. :-)
Per instrument "hard" aan de database toevoegen tot welk onderhoudsgroepje ze behoren wil ik eigenlijk voorkomen. Dan ga ik namelijk eerder meer dan minder handwerk voor onze instrumentenbeheerder creeren. Bovendien is dat een hele bewerkelijke oplossing, omdat het aantal subgroepjes niet vast staat. Het ene instrument gaart eens in de drie jaar op onderhoud, het andere eens in de vijf en het aantal instrumenten per type verschilt sterk (20 klarinetten vs. 1 set pauken bijvoorbeeld).
Ook andere oplossingen zijn in mijn gedachten al de revue gepasseerd, maar steeds loop ik vast.
Ik denk ongetwijfeld ergens veel te moeilijk. Heeft een van jullie een of meer handige tips?
Alvast heel erg veel dank!
Inleiding
Voor mijn muziekvereniging heb ik software ontwikkeld waarin we nagenoeg alle relevante zaken registreren. Daar hoort ook een administratie van instrumenten bij.
Er wordt bijgehouden welke instrumenten er worden onderhouden en welk type onderhoud het betreft: periodiek onderhoud of incidenteel onderhoud. Het idee van periodiek onderhoud is dat - zeker bepaalde typen instrumenten - hoe dan ook om de zoveel tijd (preventief) onderhoud nodig hebben.
Voor het periodieke onderhoud is een (eenvoudig) plan opgevat. Dit komt er in de kern op neer dat voor instrumenten een onderhoudsfrequentie is bedacht en dat vervolgens het aantal instrumenten over die onderhoudsperiode wordt verdeeld. Dus stel dat we 18 klarinetten hebben en de standaard onderhoudsfrequentie is eens per 3 jaar, dan zijn ieder jaar dus 6 klarinetten aan de beurt.
Voor de helderheid: het is geen optie om alle instrumenten tegelijkertijd te laten onderhouden, omdat je dan alle muzikanten wekenlang hun instrument ontneemt. :-). Juist het roulatiesysteem maakt dat er waar nodig tijdelijk een leeninstrument of een instrument op voorraad kan worden gebruikt.
Op dit moment staat nog op papier wanneer welk instrument aan de beurt is. Dat is natuurlijk heel bewerkelijk. En zonde; de administratie is immers in de basis al digitaal.
Het doel
Dat moet slimmer kunnen, dacht ik. Het doel is simpel: de computer moet in feite 'met een druk op de knop' een lijst instrumenten presenteren die nu aan de beurt zijn voor periodiek onderhoud. En ik kom een heel eind, maar ik loop toch enigszins vast. Mijn voorgevoel zegt dat een frisse blik van jullie wonderen gaat doen.
Huidige tabelstructuur
De onderliggende tabelstructuur is - voor zover relevant - op dit moment als volgt. Ik doe het maar even in de vorm van query's, mochten jullie het liever anders zien dan hoor ik graag.
Een tabel instrumentenlijst.
Deze tabel bevat de kerngegevens van alle instrumenten.
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| -- Table structure for table `instrumentenlijst` -- CREATE TABLE IF NOT EXISTS `instrumentenlijst` ( `InstrumentID` int(11) NOT NULL AUTO_INCREMENT, `InstrumentTypeID` int(11) DEFAULT NULL, `Serienummer` varchar(50) DEFAULT NULL, `MerkID` int(11) DEFAULT NULL, `Aanschafdatum` date NOT NULL, `Aanschafprijs` double DEFAULT NULL, `Verzekerde_waarde` decimal(6,2) DEFAULT NULL, `Aantal jaren afschrijving` int(11) DEFAULT NULL, `OnderhoudsID` int(11) DEFAULT NULL, `Priveeigendom` enum('ja','nee') NOT NULL DEFAULT 'nee', `Opmerkingen` text, `Gewijzigd` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`InstrumentID`), KEY `Gewijzigd` (`Gewijzigd`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 CHECKSUM=1 AUTO_INCREMENT=335 ; |
Een tabel instrumenttypes
SQL:
1
2
3
4
5
6
7
8
9
10
| -- Table structure for table `instrumenttypes` -- CREATE TABLE IF NOT EXISTS `instrumenttypes` ( `InstrumentTypeID` int(11) NOT NULL AUTO_INCREMENT, `Instrumenttype` varchar(50) DEFAULT NULL, `Gewijzigd` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`InstrumentTypeID`), KEY `Gewijzigd` (`Gewijzigd`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 CHECKSUM=1 AUTO_INCREMENT=79 ; |
Een tabel instrumenten_onderhoudshistorie.
Deze tabel bevat op regelniveau details over uitgevoerde reparaties, hetzij periodiek, hetzij incidenteel.
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| -- Table structure for table `instrumenten_onderhoudshistorie` -- CREATE TABLE IF NOT EXISTS `instrumenten_onderhoudshistorie` ( `InstrumentonderhoudshistorieID` int(11) NOT NULL AUTO_INCREMENT, `InstrumentID` int(11) NOT NULL, `Datum` date NOT NULL, `Bedrag` decimal(6,2) NOT NULL, `ReparateurID` int(11) NOT NULL, `type` enum('periodiek','incidenteel') NOT NULL DEFAULT 'incidenteel', `Omschrijving` text NOT NULL, `Timestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`InstrumentonderhoudshistorieID`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=183 ; |
En de tabel instrumenten_onderhoud_standaardfrequentie.
In deze tabel wordt per instrumenttype de standaard onderhoudsfrequentie ingevoerd (bijv. klarinetten per 36 maanden, trombones per 96 maanden). Ook wordt de het aantal rondes per frequentie ingevoerd. Dus bij een termijn van 36 maanden en 3 rondes per keer, vindt er effectief jaarlijks onderhoud plaats op 1/3 van de instrumenten van dat type.
SQL:
1
2
3
4
5
6
7
8
9
10
| -- Table structure for table `instrumenten_onderhoud_standaardfrequentie` -- CREATE TABLE IF NOT EXISTS `instrumenten_onderhoud_standaardfrequentie` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `InstrumentTypeID` int(11) NOT NULL, `Frequentie_maanden` int(11) NOT NULL DEFAULT '36', `Rondes_per_frequentie` int(11) NOT NULL DEFAULT '3', PRIMARY KEY (`ID`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ; |
Mijn huidige oplossing
Mijn huidige oplossing komt er op neer dat ik een query heb opgezet waarbij
- alle instrumenten van een bepaalde type
- die niet binnen nu en de voor dat type geldende standaardtermijn al zijn onderhouden
- en beperkt tot een aantal dat gelijk staat aan 1/x deel van het aantal instrumenten van dat type (waarbij x de rondefrequentie is)
Het idee is dat de PHP-code
- in een where loop de tabel instrumenttypes uitleest
- voor ieder instrumenttype het juiste getal invult waar nu in onderstaande query A1.InstrumentTypeID=3 staat
- voor ieder instrumenttype het getal 6 waar nu in onderstaande query LIMIT 0,6 staat vervangt door het voor dat type juiste maximum (dus bij 18 klarinetten in drie sub-groepjes 6)
- alles wordt samengevoegd met UNION ALL tot een hele grote query
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| SELECT *, (SELECT Datum FROM `instrumenten_onderhoudshistorie` WHERE type='incidenteel' AND `instrumenten_onderhoudshistorie`.InstrumentID = A1.InstrumentID ORDER BY `instrumenten_onderhoudshistorie`.`Datum` DESC LIMIT 0 , 1) as laatste_inc_onh, (SELECT Datum FROM `instrumenten_onderhoudshistorie` WHERE type='periodiek' AND `instrumenten_onderhoudshistorie`.InstrumentID = A1.InstrumentID ORDER BY `instrumenten_onderhoudshistorie`.`Datum` DESC LIMIT 0 , 1) as laatste_per_onh, (select (count(A1.InstrumentID) / B1.Rondes_per_frequentie) from instrumentenlijst as A1 JOIN `instrumenten_onderhoud_standaardfrequentie` B1 ON ( A1.InstrumentTypeID = B1.InstrumentTypeID )) as max_aantal FROM `instrumentenlijst` A1 JOIN `instrumenten_onderhoud_standaardfrequentie` B1 ON ( A1.InstrumentTypeID = B1.InstrumentTypeID ) WHERE A1.InstrumentID NOT IN (SELECT InstrumentID FROM `instrumenten_onderhoudshistorie` WHERE `type`= 'periodiek' AND (TIMESTAMPDIFF(MONTH,`Datum`,NOW()) < B1.Frequentie_maanden)) AND A1.InstrumentTypeID=3 ORDER BY laatste_per_onh DESC LIMIT 0,6 |
Vraag 1: kan dit niet eenvoudiger?
Deze oplossing levert een gigantisch lange query op. Mijn voorgevoel zegt dat dit ongetwijfeld korter en slimmer moet kunnen. Heeft iemand daarvoor wellicht de gouden tip?
De UNION heb ik nodig omdat ik in de WHERE filter op instrumenttype. Dat heb ik echter nodig, omdat ik anders ofwel
- met de LIMIT op het slot: een te korte lijst met allerlei verschillende instrumenttypes krijg;
- zonder de LIMIT op het slot: de totale lijst van alle nog te onderhouden instrumenten krijg, zonder rekening te houden met de verdeling in sub-termijnen;
Vraag 2: hoe ga ik slimmer om met dynamisch wijzigende gegevens?
Een ander punt is dat deze query - naast de omvang - een ander groot nadeel heeft: het dynamische effect ervan. Daarmee doel ik op het effect dat de query niet corrigeert voor al in deze periode uitgevoerd periodiek onderhoud.
Dus als ik vandaag een lijst opvraag van instrumenten die "aan de beurt" zijn, deze instrumenten laat onderhouden en dat onderhoud invoeren, dan zijn na die invoer volgens het systeem ogenblikkelijk alweer de volgende instrumenten aan de beurt. Terwijl ik daarvoor nu juist die standaard verdeling van onderhoudsfrequenties voor had bedacht.
Ik heb dit laatste proberen te corrigeren met een WHERE-voorwaarde die detecteert of er in de periode tussen nu en de onderhoudsperiode per subgroep (dus 12 maanden in het voorbeeld) periodiek onderhoud is geweest. Dan wordt echter geen enkel instrument meer getoond zodra er 1 instrument voor periodiek onderhoud is ingevoerd. Dat is niet handig; het moet niet zo zijn dat als de instrumentenbeheerder een dag later nog even wil dubbelchecken welke instrumenten ook alweer aan de beurt zijn dat dan de lijst alweer op 0 staat. Of geforceerd wordt van lijstjes printjes te maken omdat hij weet dat zodra er 1 onderhoud wordt ingevoerd de lijst weg is. Ik wilde juist van het papier af. :-)
Per instrument "hard" aan de database toevoegen tot welk onderhoudsgroepje ze behoren wil ik eigenlijk voorkomen. Dan ga ik namelijk eerder meer dan minder handwerk voor onze instrumentenbeheerder creeren. Bovendien is dat een hele bewerkelijke oplossing, omdat het aantal subgroepjes niet vast staat. Het ene instrument gaart eens in de drie jaar op onderhoud, het andere eens in de vijf en het aantal instrumenten per type verschilt sterk (20 klarinetten vs. 1 set pauken bijvoorbeeld).
Ook andere oplossingen zijn in mijn gedachten al de revue gepasseerd, maar steeds loop ik vast.
Ik denk ongetwijfeld ergens veel te moeilijk. Heeft een van jullie een of meer handige tips?
Alvast heel erg veel dank!