Ontbrekende en dubbele rijen bij ophalen gegevens uit AFAS Profit met GetConnector

Voor AFAS Profit 15 werkte AFAS voor API (connectoren) met ANTA (“AFAS Next Technology Architecture”).

Met ingang van AFAS Profit 15 wordt ANTA als tussenlaag overgeslagen en werken de connectoren rechtstreeks via SQL op de database zoals beschreven in de AFAS help.

We halen via GetConnectors met Invantive SQL data op uit Profit en de page size staat hierbij op 50.000 in plaats van de standaard 100 (dit in afstemming met AFAS support).

Echter, voor connectoren waar meer dan de page size aan rijen terugkomt uit AFAS komen sommige rijen dubbel terug en ontbreken andere rijen. Dit probleem treedt op bij tenminste 2 GetConnectoren, elk met grofweg 300.000 - 400.000 rijen.

Als ik de data in stukjes ophaal kleiner dan de page size door bijvoorbeeld de volgende code uit te voeren:

select *
from   TABEL
where debiteur_nummer like '00%'
union all
...
union all
select *
from   TABEL
where  debiteur_nummer like '99%'

dan zijn alle rijen wel uniek.

Van AFAS heb ik begrepen dat de sortering meegegeven moet worden omdat anders rijen kunnen ontbreken/dubbel zijn.

Hoe kan ik de juiste gegevens ophalen met Invantive SQL?

Het beschreven probleem is onbekend.

De genoemde API documentatie geeft wel aan dat de sortering anders kan zijn door de overgang van client-code naar databaseserver afhandeling, maar niet dat de API dan verkeerde uitkomsten teruggeeft. Ik zou ook verwachten dat een financieel systeem dan een error zou geven als sortering verplicht is; je bent anders niet aantoonbaar in-control als je niet geborgd hebt dat de gegevens consistent, volledig en herleidbaar zijn.

Invantive SQL maakt gebruik van skip en take met JSON berichten (documentatie). De gegevens worden dus in pagina’s opgehaald, zodat je al gegevens kunt verwerken terwijl niet alles opgehaald is.

Je kunt de daadwerkelijk uitgewisselde AFAS API calls terugzien in bijvoorbeeld het scherm Session I/Os op Invantive Cloud of met de volgende query direct na uitvoering van je SQL statements:

select *
from   sessionios@datadictionary

Suggestie is om als eerste de standaard page size van Invantive SQL nogmaals te proberen. De standaard paginagroot op AFAS API die Invantive SQL gebruikt is 100. Die 100 was ooit ook de grootst mogelijke waarde; hogere waardes werden silently teruggezet op 100, maar ik kan dit zo vlot niet in de documentatie van AFAS zelf terugvinden anders dan dat als je de take niet meegeeft, dat alleen de eerste 100 regels teruggeven worden.

Wat ik wel lees in de documentatie, is het vetgedrukte:

Skip/Take

URL/connectors/GetConnectorName?skip=0&take=20

The Skip/Take operator offers the option of getting data in bits. If the operator is not included, the first 100 lines will always be returned. If you set both skip and take to '-1’, all lines will be retrieved (we do not recommend this!).

Always use skip/take in combination with sorting. The sorting may otherwise change during the retrieval of the data.

Er is geen versie-informatie zichtbaar of dit een recente wijziging is, maar het beschreven gedrag lijkt wel aan te sluiten bij je ervaringen.

Probeer svp eerst met de standaard page size en laat je ervaring weten.

Ik zal intern navragen of er meer over bekend is qua mogelijke oorzaken.

Als workaround kun je de volgende twee opties uitproberen.

Via SQL:

select *
from   range(100,0)@datadictionary
join   TABEL
on     debiteur_nummer like lpad(to_char(rge.value), 2, '0'))

Via PSQL:

create or replace table doel@inmemorystorage
as
select *
from   TABEL
where  1 = 0

begin 
  for i in 0..99 
  loop 
    insert into doel@inmemorystorage
    ( ... 
    )
    select ...
    from   TABEL
    where  debiteur_nummer like lpad(to_char(i), 2, '0'))
    ;
  end loop; 
end;

Aanvulling: ook in een ander stuk documentatie staat dat een sortering meegegeven moet worden naar AFAS tegenwoordig:

Gebruik altijd een sortering bij het uitvoeren van een opdracht met skip/take. Sorteer bij voorkeur op een uniek veld, zoals factuurnummer of medewerkercode, of een combinatie van velden.

N.a.v. deze laatste opmerking genereert Invantive SQL een sortering naar beste eer en geweten in de laatste 20.0 en 20.1 BETA releases.

Er is zojuist een test gedaan met de page size 100. Hier komen meer dubbele rijen terug dan voorheen met de 50.000 rijen.

1 like

Bedankt voor controle. Dat is vervelend. Als ik mag gokken, dan lijkt het er op dat AFAS tussen pagina’s (take) niet meer gegarandeerd state vasthoudt en ook geen ordening standaard toevoegt en ook geen error geeft als application control als je het vergeet.

We hebben aan Invantive SQL een connectorattribuut toegevoegd analysis-enforce-row-uniqueness die indien geactiveerd van een resultset controleert of alle rijen uniek zijn. Zo nee, een error geeft.

Die attribuut kun je niet altijd aan laten staan omdat er sowieso tabellen zijn op talrijke platforms die geen set maar een bag zijn, maar is wel een manier om het te valideren of het probleem opgelost is en bij terugkomst het ook snel analyseerbaar te maken.

We hebben daarna een default sortering toegevoegd op de GetConnectoren. Ik heb nog wat vraagtekens of de sortering moet gaan over een unieke sleutel of dat die over een niet-uniek veld moet gaan. Ik vermoed dat bij sorteren over alleen een niet-uniek veld met lage cardinaliteit het probleem blijft bestaan, maar alleen minder vaak terugkomt. De AFAS documentatie beschrijft daar nog niks over, maar vanuit het gedrag van de AFAS API verwacht ik dat als je alleen sorteert op bijvoorbeeld een boolean veld je nog steeds dubbelen en verdwenen rijen zult meten. We gaan het zien.

De AFAS metadata bevat weinig informatie om te bepalen of iets een zinvolle sortering is. Je kunt niet zien of een veld verplicht is bijvoorbeeld, of het een onderdeel van een primaire/unieke sleutel is voor de view en of het veld in de AFAS database überhaupt geïndexeerd is. Zeker met grote tabellen wil je niet een order by uiteindelijk opwekken die begint met een aantal velden die niet geïndexeerd zijn. Dan moet hij telkens een ordening opbouwen vermoed ik.

Voor de sortering worden alle velden meegenomen mits:

  • Als het een tekst is, alleen als de lengte bekend is en maximaal 250 tekens om te voorkomen dat op LOB’s gesorteerd wordt.
  • Als het een getal is, alleen als er geen cijfers achter de komma staan.
  • De rest volledig.

De volgorde van de velden in de sortering is:

  • Indien beschikbaar Administratie_nummer (3 cijfers).
  • Veld eindigt met id (en heeft geen cijfers achter de komma).
  • Daarna de datum/tijd velden.
  • Daarna de Guid’s.
  • Daarna de Int64, Int32 en Int16.
  • En daarna de rest.

Tenslotte wordt na toepassen van de volgorde het geschatte aantal bytes in de waardes beperkt tot 75% van 8062.

De toegevoegde sortering vertraagt de API’s wel erg lijkt het. De doorvoorsnelheid vanuit AFAS voor resultsets met paar honderdduizend rijen is een factor 5 lager dan zonder sortering.

Workaround voor het performanceverlies zou nog kunnen zijn om ook een filter toe te voegen op basis van de ordening, zodat de API call voor een pagina specifiek aangeeft vanaf welke waardes er opgehaald mag worden, maar dat vereist testen tegen het daadwerkelijk gedrag. Hetzelfde geldt voor automatisch splitsen in deelgroepen zoals je zelf handmatig al hebt gedaan. Dat kan op administratie als partitie, maar ook daarnaast of in plaats daarvan op een subpartitie zoals debiteurnummer. Het ontbreken van statistieken over de data voor de eerste keer gegevens ophalen maakt dit wat lastiger.

Al met al voelt deze aanpak voor mij nog steeds redelijk onbetrouwbaar op Profit 15. Uiteraard is het mogelijk om de sortering handmatig te gaan opgeven bij elke API call, maar dat betekent dat een gebruiker of consultant die Invantive SQL gebruikt verstand moet hebben van de AFAS API terwijl de belofte van Invantive SQL is dat alle draaiende onderdelen onder de motorkap blijven. Voor doorgewinterde AFAS-kenners is dat geen punt, maar veel gebruikers bouwen ook koppelingen tussen meerdere pakketten voor gegevensuitwisseling en dan is het macro- en micro-economisch beter als je met minder kennis meer resultaat kunt bereiken.

Mocht fout gaan door de toegevoegde sortering, dan kun je de toegevoegde sortering uitschakelen via de AFAS-specifieke connectorattribuut analysis-add-default-sort-get-connectors. Standaard heeft deze de waardes true, maar stelt je wilt geen sortering meegeven aan Get Connector API requests dan geef je eerst op:

set analysis-add-default-sort-get-connectors false

en daarna voer je de queries uit.

1 like

Getest met versie 20.0.117.

Geen dubbele rijen meer zichtbaar. Wel adviseer ik de Guid’s als eerste te gebruiken in de sortering en daarna Administratie_nummer, ect… Dit zijn de ID’s van Afas.

Excuus, mijn uitleg was niet scherp genoeg geformuleerd. AFAS Profit geeft in de metadata (GetMetaInfo) maar zes datatypes retour en GUID is daar er geen van. De genoemde sorteringsvolgorde bovenstaand met daarin GUID mag je ook lezen zonder:

Daarna de Guid’s

aangezien die nooit terug zullen komen. AFAS Profit geeft GUIDs terug als een tekstveld. Dat is de reden dat we in tweede plaats sorteren op velden eindigend op id.

We hebben helaas geen inzicht op dit moment of Administratie_nummer in de primaire sleutels en daaraan gekoppelde indexen staat. Als een AFAS-kenner dat weet: voeg s.v.p. een reactie toe.