Array aanvullen binnen een functie die zichzelf herhaald

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Bas van de Ven

Bas van de Ven

12/05/2023 23:10:06
Quote Anchor link
Elk record in tblSchaap heeft een veld volwId. Dit veld verwijst naar tblVolwas waar de moeder (mdrId) en vader (vdrId) van een schaap uit tblSchaap is vastgelegd. De moeder en vader hoeft niet te zijn vastgelegd. 1 van de velden mdrId en vdrId (of beiden) kunnen dus leeg zijn.

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)
PHP script in nieuw venster Selecteer het PHP script
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

<?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
 
PHP hulp

PHP hulp

03/03/2024 16:27:50
 
Ward van der Put
Moderator

Ward van der Put

13/05/2023 12:31:36
Quote Anchor link
Je maakt logische fouten die lijken op een overtreding van het interface segregation principle (ISP), de I in SOLID:

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.
 
Bas van de Ven

Bas van de Ven

13/05/2023 13:18:35
Quote Anchor link
Is geslacht in jouw reactie een willekeurig voorbeeld? Geslacht is iets dat vastligt in tblSchaap. Ik heb het m.i. niet over geslacht. Hoogstens over 2 dieren die 1 ander dier op de wereld hebben gezet. Voor het gemak moeder en vader.
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?
 
Willem vp

Willem vp

13/05/2023 14:37:46
Quote Anchor link
Dit is een klassieke fout in recursief programmeren. Je mist eigenlijk alle resultaten van de tweede recursie en verder, omdat je de return-waarde van de recursieve aanroep van zoek_volgende_generatie() niet toekent aan $Array. Daardoor zal de functie uiteindelijk de oude waarde van $Array returnen. Is eenvoudig op te lossen met
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
    if(isset($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)
PHP script in nieuw venster Selecteer het PHP script
1
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

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)
PHP script in nieuw venster Selecteer het PHP script
1
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

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
 
Bas van de Ven

Bas van de Ven

13/05/2023 17:49:58
Quote Anchor link
Recursieve queries is nieuw voor mij maar daar zit wel de oplossing van mijn doel dat ik wil bereiken.
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.
 
Ozzie PHP

Ozzie PHP

13/05/2023 18:14:37
Quote Anchor link
>> Ooien die moeilijk drachtig worden wil je juist verkopen.

Hopelijk krijgen ze dan alsnog ergens anders een mooi bestaan en wordt daarop toegezien.
 
Ad Fundum

Ad Fundum

14/05/2023 08:54:04
Quote Anchor link
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.

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>)
Gewijzigd op 14/05/2023 09:08:46 door Ad Fundum
 
Willem vp

Willem vp

14/05/2023 12:36:29
Quote Anchor link
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
 
Ward van der Put
Moderator

Ward van der Put

14/05/2023 18:10:32
Quote Anchor link
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.
 
Bas van de Ven

Bas van de Ven

14/05/2023 19:03:26
Quote Anchor link
Dit is inmiddels mijn resultaat op de webpagina

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.
 
Ad Fundum

Ad Fundum

16/05/2023 11:47:00
Quote Anchor link
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.
 



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.