Uit nader onderzoek op basis van consulting services blijkt het volgende:
Aantal rijen in januari
Bij het opvragen van het aantal rijen in januari middels onderstaande query komt het getal 5509:
select count(*)
from range@datadictionary(8, 1) rge
join DataService.BITool.Declaratieregels(jaar => 2024, week => rge.value) drl
on month(drl.decldatum) = 1
Hierbij is de aanname gedaan dat de maand gelijk is aan de kalendermaand van de declaratiedatum (het veld decldatum
). De periode qua weken is bewust breed genomen met 8 weken zodat er geen randgevallen zijn.
Echter, de volgende query op basis van tabelfunctieparameter maand
gelijk aan 1 geeft aan dat er maar 5154 regels zijn:
select count(*)
from DataService.BITool.Declaratieregels(jaar => 2024, maand => 1) drl
Er is dus een wezenlijk verschil in uitkomst.
Plaatsing 10558589
Een diepere analyse is uitgevoerd op basis van plaatsingsnummer uit Easyflex. Het plaatsingsnummer van de declaratie 116861652 van registratie 4280344 is 10558589 (bepaald via de bovenstaande queries).
Specifieke filtering op deze plaatsing via de tabelfunctieparameter plaatsingnummer
geeft 16 rijen:
select drl.*
from DataService.BITool.Declaratieregels(jaar => 2024, maand => 1, plaatsingnummer => 10558589) drl
maar zonder een tabelfunctieparameter met filtering alleen in de UniversalSQL-driver (dus client-side) komen er maar 15 rijen uit:
select drl.*
from DataService.BITool.Declaratieregels(jaar => 2024, maand => 1) drl
where plaatsingnummer = 10558589
Merk op dat de Easyflex UniversalSQL-driver op dit moment niet automatisch de where
-clause omschrijft naar een tabelfunctieparameter zoals bijvoorbeeld bij Teamleader Focus wel vaak gebeurt (zie bijvoorbeeld Teamleader V2 performance improvements in server-side filtering of date columns).
Om uit te sluiten dat de where
-clause niet juist geimplementeerd is in Invantive UniversalSQL, is ook de volgende variant uitgevoerd. Deze gaf echter 16 rijen retour, dus het client-side filter heeft geen merkbare invloed en het wel/niet meegeven van de tabelfunctieparameter wel:
select drl.*
from DataService.BITool.Declaratieregels(jaar => 2024, maand => 1, plaatsingnummer => 10558589) drl
where plaatsingnummer = 10558589
Paginering
Easyflex werkt met paginering. Indien paginering niet juist geimplementeerd is binnen de UniversalSQL-driver voor Easyflex dan zou dat kunnen leiden tot de gesignaleerde afwijking. Bij problemen met paginering zijn de twee meest waarschijnlijke oorzaken:
- geen paginering gebruikt terwijl die er wel moet zijn,
- gaten tussen pagina’s waar meestal 1 rij tussen pagina’s “verdwijnt”.
De paginagrootte van Easyflex voor de achterliggende API is 5000 (zie https://easyflex.atlassian.net/wiki/spaces/WEBDATAKLNT/pages/530219856/5.+BI-tool+dataservice+operaties).
Een code review van de Invantive UniversalSQL-driver voor Easyflex toont aan dat paginering aanwezig is. De driver geeft geen totenmet
mee en altijd een vanaf
plus de maximale paginagrootte (per API mogelijk anders).
Na ontvangst van een pagina met gegevens wordt gekeken of de hoeveelheid data overeenstemt met de maximale paginagrootte. Zo ja, dan wordt een volgende pagina opgehaald. Zo nee, dan is de dataset compleet.
Merk op dat de Invantive UniversalSQL-engine “streaming” is over de gehele keten zolang er geen groepsfuncties zijn (zoals count
); er worden dus al rijen teruggegeven voordat de volledige dataset verzameld is.
Het aantal rijen voor maand 1 is 5154, terwijl er gemeten via de weekmethode 5509 uitkomen. Dit is ruim meer dan de maximale 5.000, dus dat duidt er op dat paginering plaatsvindt. Tenslotte kunnen er conform documentatie maar 5000 rijen per pagina terugkomen.
Het ontbrekende aantal van 355 rijen is significant meer dan het standaardgat dat soms in een algoritme optreedt doordat er inclusief of exclusief grenzen gewerkt wordt. In dat geval zou de afwijking 1 zijn; 5509 rijen zouden twee pagina’s behoeven, waardoor bij het verkeerd inclusief/exclusief werken 2 - 1 = 1 rij theoretisch zou kunnen wegvallen.
Een nader onderzoek van de daadwerkelijk uitgevoerde sessie I/O’s laat zien dat voor 5154 rijen echter maar een API-call nodig was via:
select call_safe_name
, parameter_list
from sessionios@datadictionary
where data_container_alias = 'efx'
order
by id desc
Mogelijk leidt het ontbreken van de parameter totenmet
tot problemen. De parameter is gedocumenteerd als “O” (optioneel), maar het zou kunnen dat bij weglating het gedrag van de API-server niet de documentatie weerspiegelt.
Er blijken echter ook 5154 rijen geretourneerd te worden als specifiek het aantal van 5.000 meegegeven wordt in de query:
select /*+ http_disk_cache(false) http_memory_cache(false) */
count(*)
from DataService.BITool.Declaratieregels(jaar => 2024, maand => 1, totenmet => 5000) drl
Dat is eigenaardig.
Rij-aantallen
Door de totenmet
te varieren is uiteindelijk een specifieke case gevonden via de volgende query:
select /*+ http_disk_cache(false) http_memory_cache(false) */
*
from DataService.BITool.Declaratieregels(jaar => 2024, maand => 3, totenmet => 31) drl
minus
select /*+ http_disk_cache(false) http_memory_cache(false) */
*
from DataService.BITool.Declaratieregels(jaar => 2024, maand => 3, totenmet => 30) drl
De verwachte uitkomst is 31 - 30 = 1 rij. Echter, er worden twee rijen geretourneerd die vrijwel identiek zijn. Het gaat bij twee regels voor beiden declid
375450208. Enkel de kolommen businessunit
en businessunitnaam
hebben een andere waarde. Dit is gecontroleerd doordat de volgende query maar 1 regel teruggeeft:
select /*+ http_disk_cache(false) http_memory_cache(false) */
* except businessunit, businessunitnaam
from DataService.BITool.Declaratieregels(jaar => 2024, maand => 3, totenmet => 31) drl
minus
select /*+ http_disk_cache(false) http_memory_cache(false) */
* except businessunit, businessunitnaam
from DataService.BITool.Declaratieregels(jaar => 2024, maand => 3, totenmet => 30) drl
Het valt hierbij op dat beide regels dezelfde waarde voor declid
hebben. Dat is eigenaardig. Mogelijk is dat een hint naar de oorzaak.
Het aantal declaraties is bekeken via:
select count(distinct declid)
from DataService.BITool.Declaratieregels(jaar => 2024, maand => 3, totenmet => 5000) drl
Dit aantal blijkt exact 5000 te zijn.
De volgende hypothese is dat het XML-formaat van de Easyflex SOAP API-server misschien verschillende XML-niveau’s kent, en dat UniversalSQL mogelijk het onderste niveau telt (waar dan 5149 regels uit volgen) en Easyflex het bovenste niveau (waar 5000 knopen zitten). Een voorbeeld daarvan is onderstaand weergegeven en is vergelijkbaar met boekstukkop met meerdere boekstukregels:
<root>
<niveau1>
<VELD1>waarde</VELD1>
<niveau2><VELD2>waarde</VELD2></niveau2>
<niveau2><VELD2>waarde</VELD2></niveau2>
</niveau1>
</root>
Echter, het door Easyflex gehanteerde XML-formaat kent maar 1 niveau en is erg eenvoudig:
...
<ds_bi_declaratieregels_result>
<item><VELD1>waarde</VELD1>...</item>
<item><VELD1>waarde</VELD1>...</item>
<item><VELD1>waarde</VELD1>...</item>
</ds_bi_declaratieregels_result>
...
Al met al kan gesteld worden dat het volgende deel van de documentatie niet juist geformuleerd is:
totenmet - xsd:int - O - Eindpunt van te selecteren regels (max = 5000 per keer).
Duiding
Het woord “regels” zou kunnen duiden op “declaratieregelnummers”.
Mogelijkerwijs is deze fout historisch ontstaan omdat de documentatie een hint geeft dat er twee semantische betekenissen mogelijk zijn van declaratieregels:
- Het is ook mogelijk dat meerdere businessunits zijn gekoppeld aan een plaatsing in Easyflex met een procentuele verdeling van omzet/kosten.
Dan wordt per afzonderlijke businessunit een declaratieregel geretourneerd met daarin het idnr van een business unit en enkel die uren, kosten en omzet die horen bij dezelfde businessunit.
Het gevonden voorbeeld heeft verschillende businessunits, maar twee keer hetzelfde declaratienummer. Volgens bovenstaande tekst zijn dat er twee declaratieregels: een per afzonderlijke businessunit.
Uit de voorgaande zinsnede lijkt het alsof de per afzonderlijke businessunit geretourneerde <item/>
ook een declaratieregel is, maar dan een ander dan gebruikt voor de telling.
Daarnaast zou het kunnen zijn dat het woord “regels” helemaal niet verwijst naar “declaratieregelnummers” of eventueel “declaratieregels” (variant 1) of “declaratieregels” (variant 2), maar gewoon naar regels uitvoerresultaat voor de paginering. Dit is bij verre het meest gebruikelijk - zo niet universeel - als industriestandaard.
Het blijft gokken; advies is om de leverancier van Easyflex de documentatie te laten bijstellen zodat:
- de tenminste twee verschillende betekenissen van “declaratieregels” gesplitst of samengevoegd worden zodat de definities eenduidig zijn;
- op vergelijkbare wijze de betekenis van alle voorkomens van “regels” te corrigeren door splitsing of samenvoeging;
- duidelijk uit de documentatie volgt wat het aantal verwachte
<item/>
elementen per pagina is;
- de werking van de software conform nieuwe documentatie is.
Zodra dit gebeurt is kan indien nog nodig de driver verbeterd worden.
Het uitgangspunt van de Invantive UniversalSQL-engine is dat aantoonbare correctheid bij resultaat zwaarder weegt dan werking uberhaupt. In de volgende release van Easyflex zal daarom een application control itgenefx026
opgenomen zijn dat bij retour van meer rijen dan verwacht een fatale fout optreedt.
Een regressietest is uitgevoerd voor alle Easyflex API’s. Het blijkt dat de volgende API’s onder hetzelfde euvel leiden (en dus meer rijen terug kunnen geven dan verwacht):