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 delavsnitt) och/eller göra testet direkt i skriptet.
Att testa textsträngar tar längre tid än numerisk testning. Ta till exempel följande uttryck:
If (Alfa= ‘ABC’, ‘ABC’, left (Alfa, 2))
Testet skulle kunna göras direkt i skriptet utan att någon flexibilitet går förlorad:
Ladda
*,
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. Detta kan göras på flera sätt, till exempel interval match, group by, peek och 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) )
Ladda
*,
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:
Ladda
*,
If (Alfa = ‘ABC’, 1, 0) as Flag
resident table_1 ;
table_3:
Ladda
Alfa,
If ( Flag = 1, Num* 1.25 , Num ) as NewNum
resident table_2 ;
table_4:
Ladda
Alfa,
Sum( NewNum ) as SumNum
resident table_3
group by Alfa ;
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
och
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.
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 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.