Förbättrad programprestanda

Inledning

Applikationens design påverkar sällan prestandan nämnvärt vid små eller medelstora QlikView-applikationer. I takt med att datamängden växer kan dock såväl tids- som minnesbegränsningar bli väldigt märkbara om applikationens design är bristfällig. Det är möjligt att smärre ändringar ger avsevärt bättre prestanda. Denna del av appendix visar några vanliga fallgropar och ger förslag på hur man kan lösa dem.

Generellt sett förbättras prestandan om man flyttar “problemet“ från applikationens objekt till den skriptdrivna databasen. Ofta får man göra en avvägning. Svarstiden förbättras och den aktuella minnesanvändningen minskas. Rekommendationerna som ges nedan ska inte ses som universella botemedel. Använd dem när de förbättrar applikationen i stort eller när de medför det där lilla extra.

Nedan följer en lista med exempel på metoder som kan användas för att komma tillrätta med dessa problem. Syftet är att illustrera problemet och visa på användbar QlikView-funktionalitet. Det går inte att ge en allmän rekommendation om vilken metod som är bäst, men ordningen i vilken exemplen presenteras ger en fingervisning.

If ( Condition(Text),…..)

If-satser som innehåller jämförelser mellan text är i allmänhet kostsamma. En lösning kan vara att mappa text till tal, exempelvis genom att använda autonumber (se exempel i föregående avsnitt) och/eller göra testet direkt i skriptet.

Att testa textsträngar tar längre tid än numerisk testning. Ta exempelvis uttrycket

If (Alfa= ‘ABC’, ‘ABC’, left (Alfa, 2))

Testet skulle kunna göras direkt i skriptet utan att någon flexibilitet går förlorad:

Load

*,

If (Alfa = ‘ABC’, 1, 0) as Flag

resident table_1 ;

Uttrycket blir då

If ( Flag = 1,’ABC’, left (Alfa, 2))

och testet blir mycket enklare att genomföra.

Sum ( If (Condition, ‘FieldName’…))

I detta fall är aggregeringen oberoende av tabelldimensionerna och resultatet fördelas över tabellens dimensioner. Problemet kan lösas genom att man antingen gör testet i skriptet och därefter aggregerar det i tabellen eller gör hela åtgärden direkt i skriptet. Det finns många tekniker för detta, till exempel interval match, group by, peek, if....then....else.

Denna lösning görs i två steg; man testar "Condition" samt aggregeringen av resultatet. Låt oss ta föregående exempel och lägga till aggregeringen

Sum ( If (Alfa= ‘ABC’, Num*1.25 , Num) )

Load

*,

If (Alfa = ‘ABC’, 1, 0) as Flag

resident table_1 ;

Uttrycket blir då

Sum ( If ( Flag = 1, Num* 1.25 , Num ) )

Aggregeringen kan även göras direkt i skriptet som i följande exempel:

tabell_2:

Load

*,

If (Alfa = ‘ABC’, 1, 0) as Flag

resident table_1 ;

table_3:

Load

Alfa,

If ( Flag = 1, Num* 1.25 , Num ) as NewNum

resident table_2 ;

 

table_4:

Load

Alfa,

Sum( NewNum ) as SumNum

resident table_3

group by Alfa ;

Observera: Aggregeringen görs över Alfa som är testets dimension.

If ( Condition, Sum(‘FieldName’)..)

Denna konstruktion har tagits med endast för att poängtera skillnaden gentemot föregående exempel. Denna aggregering är helt kontextuell och innebär i princip inga problem med prestandan.

If ( Condition1, Sum(‘FieldName’), If (Condition2, Sum(‘FieldName’)……..

Logiken i det nästlade If... then else... är konceptuellt sett enkel men kan ofta vara svår att administrera. Vi har stött på fall med hundratals nästlade nivåer. Detta blir både minnes- och processorintensivt. Ofta kan "Villkoren" ersättas genom transformering. Ett typexempel är aggregeringen av quantity*price där price är variabel. Detta kan lösas med s.k. extended interval match. Om två villkor, exempelvis ”A AND B” ska uppfyllas kan testet ersättas av villkor ”C”.

Exempel:

sum((GAC12_STD_COST * GAC15_EXCHANGE_RATE) * GIV24_DISP_QTY)

Replaces

Sum(

If((GAC12_EFCT_DT<= GIV23_REJ_DT and

GAC12_EXPIRE_DT>GIV23_REJ_DT) and

(GAC15_EFCT_DT<= GIV23_REJ_DT and GAC15_EXPIRE_DT>GIV23_REJ_DT),

GAC12_STD_COST * GAC15_EXCHANGE_RATE) * GIV24_DISP_QTY,

Null()))

and

Sum(

If(GAC12_EFCT_DT<= GIV23_REJ_DT,

If(GAC12_EXPIRE_DT>GIV23_REJ_DT,

If(GAC15_EFCT_DT<= GIV23_REJ_DT,

If(GAC15_EXPIRE_DT>GIV23_REJ_DT,

(GAC12_STD_COST * GAC15_EXCHANGE_RATE) * GIV24_DISP_QTY,

Null())))))

genom att läsa in fälten GAC12_STD_COST och GAC15_EXCHANGE_RATE som långsamt förändrade dimensioner.

​Se: Användning av utökad intervalmatch-syntax för att lösa problem med långsamt förändrade dimensioner

Sortera text

QlikView utvärderar automatiskt om ett fält ska behandlas som numeriskt, text eller allmänt. Fält som utvärderas som text sorteras som text vilket är den mest långsamma sorteringsåtgärden. Detta kan ändras manuellt genom att man sorterar i laddningsordning. Man behöver inte stänga av sorteringen av listboxar o.d.

QlikView sorterar strängar där bokstäver och tal blandas i alfanumerisk ordning. Med andra ord sorteras tal i värdeordning medan icke-tal sorteras i ASCII-ordningsföljd. Detta skiljer sig från den traditionella sorteringen för ASCII. Exempel:

ASCII sort Alphanumeric sort
A1 A1
A10 A4
A11 A5
A30 A6
A4 A10
A5 A11
A6 A30

Dynamiska namnlister och textobjekt

Dynamiskt beräknade uttryck kan placeras nästan varsomhelst där man kan skriva in text. Vilka resurser som krävs för att utvärdera ett uttryck beror dock på vilken miljö det befinner sig i. Uttryck i diagram och tabeller som har definierats i uttrycksdialogen kan bara beräknas när objektet visas samt när data ändras. Ingen beräkning görs exempelvis när objektet är minimerat.

Å andra sidan, om objekttiteln är beräknad, görs denna beräkning vid varje ändring av data. Det finns även flera sätt för att ange villkor för visning, villkor för beräkningar osv. Dessa test görs också alltid.

Vissa uttryck är mer kostsamma än andra och blir självfallet än mer kostsamma ju oftare de måste utvärderas. Den asynkrona beräkningen har förändrat detta beteende och kanske har denna påverkan blivit mer tydlig i dina applikationer.

Tidsfunktionerna, t.ex. Now() och Today() kommer alltid att utvärderas när en ny beräkning måste göras. Särskilt funktionen Now() kan bli kostsam eftersom den orsakar en ny beräkning av dokumentet varje sekund.

Exempel:

If ( ReloadTime()+3>Now(), 'Old Data', 'New Data')

Där man skulle kunna ändra till

If ( ReloadTime()+3>Today(), 'Old Data', 'New Data')

Gör ett enkelt test och sätt in uttryck i textboxar. Försök därefter att ändra storlek på textboxen när det står Now() i den.

Makro-utlösare (“on change”)

Makron kan ställas in så att de utlöses av i stort sett vilken händelse som helst i applikationen. Var försiktig med rekursiva händelser där en händelse startar nästa som i sin tur startar nästa osv.

Hjälpte den här informationen?

Tack för att du berättar det här. Är det något du vill säga om det här avsnittet?

Varför var informationen inte till hjälp och hur kan vi förbättra den?