Array aanvullen binnen een functie die zichzelf herhaald
Het uiteindelijke doel is achterhalen hoeveel procent een schaap afkomstig is van het ene ras en hoeveel van andere rassen. Of dit doel uiteindelijk haalbaar is wil ik nu even in het midden laten.
Wat ik als eerst wil bereiken is een functie die opzoek gaat naar een ouder en zich blijft herhalen tot er geen ouder meer wordt gevonden. Dus zoek moeder, zoek vervolgens oma mits deze bestaat zoek vervolgens naar overgrootoma mits deze bestaat enz. Tot er geen generatie meer wordt gevonden.
Dit heb ik gebouwd. Echter vult de array zich met 2 elementen (generaties) terwijl ik weet dat er 3 generaties bestaan.
Waarom herhaalt de functie zichzelf maar 1x? Doe ik iets fout in de functie?
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
include "ConnectieMetDatabase.php";
function zoek_ma($datb,$VolwId) {
$zoek_moeder = mysqli_query($datb,"
SELECT mdr.volwId, mdr.rasId, mdr.schaapId
FROM tblVolwas v
left join tblSchaap mdr on (v.mdrId = mdr.schaapId)
WHERE v.volwId = $VolwId
");
if($zoek_moeder)
{
while ($row = mysqli_fetch_assoc($zoek_moeder)) { $moeder = array($row['volwId'], $row['rasId'], $row['schaapId']); }
return $moeder;
}
return FALSE;
}
function zoek_volgende_generatie($datb,$Array,$input) {
$ouders_van_schaap = zoek_ma($datb,$input)[0];
$Array[] = $ouders_van_schaap;
if(isset($ouders_van_schaap)) {
zoek_volgende_generatie($datb,$Array,$ouders_van_schaap);
}
return $Array;
}
$ouders = array($txtVolw); // $txtVolw is een input veld om te testen met een waarde uit de database. in dit geval de waarde 11020
$generaties = zoek_volgende_generatie($db,$ouders,$txtVolw);
echo'functie = '; var_dump($generaties); echo'<br>';
?>
Het resultaat is.
functie = array(2) { [0]=> string(5) "11020" [1]=> string(4) "1664" }
Er is nog een derde generatie dus ik verwacht.
functie = array(3) { [0]=> string(5) "11020" [1]=> string(4) "1664" [2]=> string(5) "10801" }
Ps. Nogmaals deze code is slechts het begin van mijn einddoel. Ik weet dat oudere generatie uit steeds meer dieren bestaat en dat ik nu enkel zoek naar de moeder van de moeder van de moeder enz.
Wat ik nu wil weten is of er een functie mogelijk is die zichzelf een x aantal keren blijft herhalen waarbij er per keer een array wordt aangevuld met een element uit betreffende generatie.
Gewijzigd op 12/05/2023 23:19:34 door Bas van de Ven
No code should be forced to depend on methods it does not use.
Je dwingt je code te zoeken naar geslacht terwijl je het ras wilt weten. Daardoor met je vaders en moeders apart afhandelen, met twee maal meer code dan nodig is, terwijl je alleen geïnteresseerd bent in ouders.
Probeer je code zó te herschrijven dat je het ras van ouders kunt bepalen, onafhankelijk van het geslacht. Lukt dat voor de ouders, dan kun je het recursief maken voor alle ouders van ouders, tot je er geen meer vindt.
Per ouder ben ik opzoek naar het ras. Het veld moeder en vader ligt vast in 1 record binnen tblVolwas. Elk record is een generatie zeg maar.
Volgens mij moet ik eerst zoeken op volwId om elke volgende gerenatie uit het verleden te zoeken in tblVolwas. Vandaar dat ik me eerst focus op volwId en nog niet op rasId uit tblSchaap. Ik dwing de code dus te blijven zoeken naar volwId. Pas als ik elke generatie weet (zeg maar volwId) kan ik zoeken naar het bijbehorend ras per generatie van enerzijds de moeder en anderzijds de vader?
Welke logische fout maak ik hierin?
Code (php)
1
2
3
2
3
if(isset($ouders_van_schaap)) {
$Array = zoek_volgende_generatie($datb,$Array,$ouders_van_schaap);
}
$Array = zoek_volgende_generatie($datb,$Array,$ouders_van_schaap);
}
(nog mooier is het wellicht als je $Array by-reference doorgeeft; dat scheelt ook een hoop stack space).
Er kunnen goede ontwerpredenen zijn om het zo te doen, maar ik snap sowieso niet goed waarom moeder en vader in een aparte tabel zijn vastgelegd. Aangezien elk schaap (voor zover mijn biologische kennis strekt) slechts 1 moeder en 1 vader heeft, zou je ook (NULLable) velden vdrID en mdrID in tblSchaap kunnen opnemen. Dat scheelt je een join.
Zelf ben ik altijd geneigd om het moeilijke werk over te laten aan de database-server. Door een recursieve query te gebruiken, hoef je alleen maar het id van een willekeurig schaap in te voeren om alle moeders en oma's in een keurig tabelletje terug te krijgen (de andere kant op kan trouwens ook, maar ik weet niet of je dat zou willen, want met een beetje pech wordt dat een erg grote tabel).
Je zou dan zoiets krijgen als:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
with recursive sheep (schaapId, mdrId, rasId) as (
select schaapId, mdrId, rasId
from tblSchaap
where schaapId = $SchaapId
union all
select s.schaapId, s.mdrId, s.rasId
from tblSchaap s
join sheep on s.schaapId = sheep.mdrId
)
select *
from sheep
select schaapId, mdrId, rasId
from tblSchaap
where schaapId = $SchaapId
union all
select s.schaapId, s.mdrId, s.rasId
from tblSchaap s
join sheep on s.schaapId = sheep.mdrId
)
select *
from sheep
In deze opzet heb ik het mdrId (en vdrId) opgenomen in tblSchaap. Ik heb nooit geprobeerd om andere tabellen te joinen in een recursieve query, dus ik kan niet inschatten of het met jouw tabelstructuur ook zou werken. Overigens moet je ook geen al te oude MySQL hebben, want recursieve queries worden pas ondersteund vanaf MySQL 8 (en MariaDB 10.2).
Toevoeging op 13/05/2023 15:38:46:
Met de oorspronkelijke tabelstructuur zou dit moeten werken als je een recursieve query wilt gebruiken:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
with recursive sheep (schaapId, mdrId, rasId) as (
select s.schaapId, v.mdrId, s.rasId
from tblVolwas v
left join tblSchaap s on s.volwId = v.volwId
where schaapId = $SchaapId
union all
select ss.schaapId, vv.mdrId, ss.rasId
from tblVolwas vv
left join tblSchaap ss on ss.volwId = vv.volwId
join sheep on ss.schaapId = sheep.mdrId
)
select *
from sheep
select s.schaapId, v.mdrId, s.rasId
from tblVolwas v
left join tblSchaap s on s.volwId = v.volwId
where schaapId = $SchaapId
union all
select ss.schaapId, vv.mdrId, ss.rasId
from tblVolwas vv
left join tblSchaap ss on ss.volwId = vv.volwId
join sheep on ss.schaapId = sheep.mdrId
)
select *
from sheep
Ik ben overigens uitgegaan van het schaapId en niet van het volwId, omdat ik dat logischer vind, maar als je de join/where-condities aanpast werkt het ook als je een volwId opgeeft.
Gewijzigd op 13/05/2023 15:40:42 door Willem vp
Bedankt. Ik kan dan inderdaad uitgaan van schaapId van het lam.
Ps.
Als een ooi wordt gedekt met een ram (moeder met vader) wordt dit vastgelegd in tblVolwas. De bevruchting (dracht) hoeft echter niet plaats te vinden waardoor er nooit een lam in tblSchaap wordt vasgelegd. Moeder en vader in tblSchaap bestaat dan ook niet als ik de ouders vastleg in tblSchaap.
Is de ooi wel drachtig dan kan de ooi meerdere lammeren werpen. Per lam wil ik weten bij welke worp (volwId) het dier hoort zodat ik bijv. ook de grootte van een worp in beeld kan brengen. Ooien die vaker een worp van 3 lammeren hebben zijn interessant om aan te houden. Ooien die moeilijk drachtig worden wil je juist verkopen. Dit verklaart een beetje mijn keuze voor tblVolwas.
Hopelijk krijgen ze dan alsnog ergens anders een mooi bestaan en wordt daarop toegezien.
Je kunt dan het aantal schapen per worp tellen met een SUM()-functie terwijl je groepeert (GROUP BY) op de ID van de moeder en de geboortedatum van de lammeren (via een JOIN).
Op dezelfde manier kan je ook het rendement van een ooi berekenen over een periode, door het aantal lammeren te selecteren van een ooi en de filteren op de geboortedatum in een bepaalde periode (WHERE geboren BETWEEN <begindatum> AND <einddatum>)
Ad Fundum op 14/05/2023 08:54:04:
Om meerlingen bij schapen of andere dieren of mensen te onderscheiden, volstaat een (geïndexeerde) datumkolom 'geboren', naast de twee eerder genoemde kolommen met het ID van de vader en de moeder.
Na de verduidelijking van Bas begin ik toch ook wel het nut in te zien van die tblVolwas. Zonder die tabel is het niet mogelijk om mislukte dekkingen uit de gegevens te halen. Om meerlingen te tellen volstaat dan een sum() op het veld volwId van tblSchaap.
Ondanks dat ik een (ex-)Dordtenaar ben weet ik niet al teveel over schapen, dus ik weet ook niet hoe in de praktijk wordt omgegaan met de geboortedatum. Stel nu dat de bevalling rond middernacht plaatsvindt, en het ene deel van de lammeren wordt geboren vóór middernacht en een ander deel erna? Worden dan alle geboortes op dezelfde datum geregistreerd of niet? Zo nee, dan is het dus niet 100% betrouwbaar om een sum() te down op de geboortedatum.
Gewijzigd op 14/05/2023 12:43:00 door Willem vp
De gemiddelde draagtijd van een schaap is 145 dagen, dus dan maakt het (technisch) niet heel veel uit of een meerling is geboren met een verschil van 1 minuut, 1 uur of 1 dag.
Voorouders van het schaap :
Werknr Geslacht Ras Moeder Ras moeder Vader Ras vader Worpgrootte
74615 ooi Rijnlam 89542 Rijnlam 43524 Rijnlam 1
37282 ram Rijnlam 74615 Rijnlam 43524 Rijnlam 3
Werknr 89542 en 43524 hebben geen volwId in tblSchaap waardoor deze niet meer worden getoond in de linker kolom Werknr.
De nauwkeurigheid van registreren bepaald wat wel en niet wordt getoond. Dat is verder niet aan mij.
Het klinkt logisch om de geboortedatum vast te leggen in tblSchaap. Toch heb ik ervoor gekozen alle datums van een schaap vast te leggen in tblHistorie met bijbehorende actie. Bijv. geboren of verkocht maar ook overgeplaatst, gespeend, medicatie, omnummeren, gedekt, drachtig enz.
Deze datums kan ik dan vanuit 1 tabel ook gebruiken in bijv. tblMeldingen. Deze wordt gebruikt bij het melden van dieren aan de overheid. Of in de tblBezet(ting) waar een dier in een 'verblijf' wordt gezet. In tblNuttig wordt het medicijnverbruik vastgelegd. Zo kan ik nog wel door blijven gaan.
Willem vp op 14/05/2023 12:36:29:
Na de verduidelijking van Bas begin ik toch ook wel het nut in te zien van die tblVolwas.
Ik nu ook, dankzij jouw post, ik had er overheen gelezen.
De dekking en dracht is een aparte gebeurtenis die je ook wilt vastleggen. In die tabel komt dan de `vader` en `moeder`, met datumtijd kolom `dekking` een NULLable datumkolom `dracht`.
In de tabel van het schaap komt dan de datumtijd kolom `geboren` en een foreign key relatie naar het het `id` veld in de dekkingstabel.
De worpgrootte moet niet in de dekkingstabel, want die blijkt uit het aantal geboren lammeren.
Het geslacht moet ook niet in de dekkingstabel, die houd je bij in de tabel van het schaap.
Het is omslachtig om de geboortedatum in een historietabel bij te houden, maar niet helemaal onlogisch. Die tblHistorie tabel is een soort logboek waar je database technisch moeilijk wat mee kan.
Zo kun je bijvoorbeeld beter nadenken over wat verkoop precies betekent. Dit is een transactie van een schaap uit je database naar ergens anders. Dan zal er ook wel iets zijn als inkoop. Wat wil je daar van weten (bijhouden)? Het zou waarschijnlijk beter zijn om dat anders in te richten dan nu het geval is.