The following statement is an incremental update, switching all registered accounts to receive both a PDF as well as XML invoice:
update /*+ http_memory_cache(false) http_disk_cache(false) */
ExactOnlineREST..Accounts@eol
set InvoiceAttachmentType = 3
where InvoiceAttachmentType != 3
limit 1000
The typical runtime is less than a second, since due to the incremental behaviour only few records will be selected. The typical API consumption is one API-call to retrieve the records and one API-call for each record to be updated.
When mandatory filtering on Exact Online is activated, an error will occur such as:
itgeneor330:
A required filter is missing.
The remote server returned an error: (400) Bad Request.
or on older releases:
itgenoda061
A GET request for this endpoint must have a $filter parameter with one of the following fields in the url:Accountant,AccountManager,AddressLine1,AddressLine2,AddressLine3,Blocked,BRIN,BSN,ChamberOfCommerce,City,Classification1,Classification2,Classification3,Classification4,Classification5,Classification6,Classification7,Classification8,Classification,Code,CodeAtSupplier,Country,Created,Email,EndDate,Fax,ID,InvoiceAccount,IsAccountant,IsReseller,IsSales,IsSupplier,MainContact,Name,Parent,Phone,Postcode,PriceList,Reseller,RSIN,SearchCode,StartDate,Status,Type,VATNumber.
The implementation of mandatory filtering leaves room for improvements. In this case, the InvoiceAttachmentType
was not included as a sufficient filter. Even worse, without mandatory filtering it could be used as a filter and could be very selective since being contained in the base table:
In a future release of Invantive SQL a workaround will be implemented. This might take the form of disabling server-side filtering altogether for columns that could previously be used for optimal performance.
For now, you can use one of the following two alternatives to cope with mandatory filtering on Exact Online.
Combine Use of Exact Online sync API and traditional API
First alternative is to offload processing to the client software using the sync APIs and combining the selection with individual update statements as previously. Using individual update statements does not introduce extra processing time since Exact Online does not support the bulk update specification of OData. The processing requirements locally will grow somewhat because the sync APIs require an update, while in the original query the SQL Server of Exact Online filtered the data.
The resulting code becomes either in PSQL:
begin
for r in
( select id
from AccountsIncremental@eol
where invoiceattachmenttype != 3
limit 1000
)
loop
update ExactOnlineREST..Accounts@eol
set invoiceattachmenttype = 3
where id = r.id
;
end loop;
end;
Or in SQL:
update /*+ http_memory_cache(false) http_disk_cache(false) */
ExactOnlineREST..Accounts@eol
set InvoiceAttachmentType = 3
where ID
in
( select /*+ low_cost */ ID
from AccountsIncremental@eol
where InvoiceAttachmentType != 3
limit 1000
)
Note the use of the low_cost
SQL-hint. Otherwise still an itgeneor330
will occur, since the AccountsIncremental
is not yet considered a low-cost table which enables early evaluation in query execution.
Fool mandatory filtering
Mandatory filtering is poorly implemented to achieve it’s goal of reaching increased use of the sync APIs. Mandatory filtering can easily be tricked into accepting a filter even when it is no real filter. Mandatory filtering ignores the selectiveness of a filter value or filter operator, so filters can easily be constructed which will return close to 100% of the data can easily be derived.
Using this fact, an optimistic approach would be to use:
update /*+ http_memory_cache(false) http_disk_cache(false) */
exactonlinerest..accounts@eol
set invoiceattachmenttype = 3
where invoiceattachmenttype != 3
and Created > to_date('19700101', 'YYYYMMDD')
Since Created
is a mandatory column with a lower-bound well above 1970, this filter should work. The resulting API call is:
…/Accounts?$select=*&$filter=Created gt datetime’1970-01-01T00:00:00’ and InvoiceAttachmentType ne 3
However, mandatory filtering no longer accepts this filter due to a bug as described above. By rewriting the query to not exclude the filter on InvoiceAttachmentType
mandatory filtering from the server-side filter, the Exact Online API can be tricked into accepting the statement:
update /*+ http_memory_cache(false) http_disk_cache(false) */
exactonlinerest..accounts@eol
set invoiceattachmenttype = 3
where invoiceattachmenttype + 1 != 3 + 1
and Created > to_date('19700101', 'YYYYMMDD')
The last statement has a significant downside: it generates hundreds of API calls like:
…/Accounts?$filter=Created gt datetime’2020-08-02T00:00:00’&$select=*&$skiptoken=guid’…’
and performs the few necessary updates at the end. This alternative implementation can easily take more than hundred times longer to run and consumes hundreds of API calls compared to the single API call of the original statement without mandatory filtering and the variant using a for
loop.
The recommended solution when confronted with mandatory filtering is to switch to the for
loop implementation.
Learn more on Exact Online mandatory filtering
More relevant topics on mandatory filtering on Exact Online are: