Het is de eerste vraag die ik hoor na het oplossen van een Oracle optimizer performance issue met grote impact.
De vraag is eigenlijk: hoe groot is het risico dat dit nog een keer gebeurt?
Het antwoord op de originele vraag is altijd: jazeker, op de tweede meestal: het risico is zeker aanwezig.
In het vervolg van het gesprek wordt de database software en zijn maker vaak aangewezen als onbetrouwbaar.
Dit is het gevolg van de onbekendheid met de database en hoe hij probeert de systemen op zijn best tot dienst te zijn.
Dat is altijd een balans tussen de effort voor het bepalen wat de beste aanpak voor een opdracht is en de effort voor het uitvoeren van die opdracht. Dit zonder te weten wat de volgende vraag aan de database is en binnen de kaders van de gestelde configuratie.
In deze laatste zin zit hem de crux.
De configuratie is meestal redelijk default en menselijke kennis van het systeem en zijn gebruik worden niet ingezet om de database te helpen.
Als een database ingericht wordt voor OLTP dan is planstabiliteit belangrijk, bij BI uitgebreide statistieken en een royale tijd voor planbepaling. Is er sprake van groei in de data? Worden tabellen gebruikt voor tijdelijke data? Verloopt het gebruik van het systeem in de dag/week (online/batch)?
Zo zijn er vele relevante vragen die kunnen helpen bij het optimaliseren van van de inrichting van de database.
Hiervoor zijn legio instrumenten, veelgebruikt zijn configuratie van databaseparameters en timing en configuratie van statistiekenbepaling. Maar ook outlines, baselines, system statistics, handmatige statistics, multi column histogrammen, hints, sql patches etc etc.
De boodschap is: denk met alle partijen goed na over je inrichting en hereik deze met enige regelmaat aan het actuele gebruik en je voorkomt het overgrote deel van de performance incidenten.
Hierbij is het belangrijk dat alle betrokken partijen hun input leveren, de dba alleen weet niet alles over de applicaties en hun gebruik en ook niet altijd alles over de belasting en ruimte in de infrastructuur.
Een goede databaseperformance met minimale incidenten is niet vanzelfsprekend zonder configuratie en beheer op maat.
Om van een bepaald jaar de erkende feestdagen van Nederland te bepalen gebruik ik al jaren hetzelfde stukje PL/SQL-code. De aankomende troonswisseling, en daarmee het wijzigen van de nationale feestdag Koninginnedag naar Koningsdag, was voor mij de aanleiding om de bewuste code eens te reviseren.
Nederland kent de volgende elf erkende feestdagen:
De ‘blauwe’ feestdagen vallen ieder jaar op dezelfde datum, de ‘groene’ feestdagen zijn alle af te leiden uit de 1e Paasdag en de dag waarop Nederland Koning(s)(inne)dag viert, wordt bepaald door de verjaardag van de regerende vorst.
1e Paasdag oftewel paaszondag is met behulp van het algoritme van J.M. Oudin (1940) te berekenen. Geldig voor de Gregoriaanse kalender, dus vanaf 1583. In de PL/SQL-code hieronder heb ik het algoritme uitgewerkt.
create or replace function easter_sunday
(p_year number := to_char(sysdate,'yyyy'))
return date
/**********************
* 2013 www.orcado.nl *
**********************/
is
l1 pls_integer;
l2 pls_integer;
l3 pls_integer;
l4 pls_integer;
l5 pls_integer;
l6 pls_integer;
l7 pls_integer;
begin
if p_year < 1583 then
return null;
end if;
l1 := trunc(p_year / 100);
l2 := mod(p_year,19);
l3 := l1 - trunc(l1 / 4);
l4 := mod(l3 - trunc((l1 - trunc((l1 - 17) / 25)) / 3) + 19 * l2 + 15,30);
l5 := trunc(l4 / 28);
l6 := l4 - l5 * (1 - l5 * trunc(29 / (l4 + 1)) * trunc((21 - l2) / 11));
l7 := mod(p_year + trunc(p_year/4) + l6 + 2 - l3,7);
return to_date('0103' || p_year,'ddmmyyyy') + l6 - l7 + 27;
end easter_sunday;
/
SQL> select easter_sunday from dual;
EASTER_SUNDAY
-------------
31-03-2013
SQL> select easter_sunday(2025) from dual;
EASTER_SUNDAY(2025)
-------------------
20-04-2025
Koninginnedag doet zijn intrede op 31 augustus 1898 toen Wilhelmina koningin van Nederland werd. Sinds 1949 is Koninginnedag op de verjaardag van koningin Juliana, 30 april. Vanaf 2014 zal Koningsdag gevierd gaan worden op 27 april, de verjaardag van dan koning Willem-Alexander. Wanneer vanaf 1949 Koning(s)(inne)dag op een zondag valt, wordt het één dag verplaatst. Tot 1980 werd Koninginnedag verplaatst naar maandag 1 mei. Sinds 1980 wordt het de dag ervoor op zaterdag gevierd. Het bepalen van Koning(s)(inne)dag heb ik als volgt uitgewerkt.
create or replace function kingsday
(p_year number := to_char(sysdate,'yyyy'))
return date
/**********************
* 2013 www.orcado.nl *
**********************/
is
l_day varchar2(4);
l_date date;
begin
if p_year < 1898 then
return null;
elsif p_year < 1949 then
l_day := '3108';
elsif p_year < 2014 then
l_day := '3004';
else
l_day := '2704';
end if;
l_date := to_date(l_day || p_year,'ddmmyyyy');
if p_year > 1948
and to_char(l_date,'dy','nls_date_language=dutch') = 'zo' then
l_date := l_date + sign(19795 / 10 - p_year);
end if;
return l_date;
end kingsday;
/
SQL> select kingsday from dual;
KINGSDAY
----------
30-04-2013
SQL> select kingsday(2025) from dual;
KINGSDAY(2025)
--------------
26-04-2025
Bovenstaande stukken code heb ik geïntegreerd in het onderstaande voorbeeld, waarin bij een opgegeven jaar de erkende feestdagen worden getoond. Condities die hierin zijn opgenomen, zijn:
Zo, in ieder geval tot de volgende troonsopvolging van prinses Amalia is de code weer up-to-date.
Lees verder in deel 2.
Gevaren in histogram endpointsBij het analyseren van een ongunstige plankeuze door de Oracle optimizer liep ik laatst tegen een situatie aan waar er een cardinaliteit van één in het plan stond waar er tienduizenden verwacht waren.
De index statistieken waren redelijk in lijn met het verwachte aantal rijen, waarom dan toch de misser in het plan?
Het euvel bleek in een histogram te huizen. De statistieken werden in de nacht berekend en er kwamen op de dag nieuwe records in de tabel die met de betreffende query weer opgehaald werden voor verwerking aan de hand van een jobid.
Het (frequency) histogram bevatte bijvoorbeeld de aantallen records voor jobid 100 t/m 120 en die dag werd job 121 gedraaid.
De optimizer ziet bij een bind peek in de hard parse het nieuwe jobid en denkt dat deze niet in de tabel voorkomt omdat deze boven de maximale high_value in het histogram ligt.
In de verdere berekening wordt hierdoor met één gerekend wat het ongunstige plan veroorzaakt heeft.
Hoewel dit gedrag redelijk natuurlijk volgt uit de informatie uit de statistieken vraag ik me toch af of er wel een situatie is waar dit positief uitwerkt. Er moeten wel heel vaak niet bestaande gegevens opgevraagd worden wil een plan dat hiervan uitgaat het gemiddeld winnen van een plan dat uit gaat van wel bestaande rijen.
apex.widget.calendar.ajax_calendarPas geleden zag ik een vraag op het APEX Forum over het refreshen van een pagina met een calendar regio.
Het probleem is dat er een LOV-popup item op de pagina staat waarvan de query in de calendar regio gebruikt maakt. Als deze LOV-popup wijzigt moet de calendar regio refreshen met de nieuwe waarde van de LOV-popup. Simpel toch? Ja, dat dacht ik ook maar degene die de vraag stelde zei al van alles geprobeerd te hebben inclusief dynamic actions etc. Nu was ze zover dat haar vraag kwam of ze de pagina kon herladen met javacode zoiets als redirect(this.URL). Iemand gaf haar netjes het antwoord op deze vraag door de juiste javascript code te geven, location.reload().
Mij leek het beter om het probleem zelf (in plaats van de workaround die ze gemaakt had aan te vullen) op te lossen, dus alleen de calendar regio te refreshen zonder de hele pagina te herladen. Apex gebruikt zelf de functie apex.widget.calendar.ajax_calendar om in de calendar te bladeren en van view te veranderen. Standaard wordt op de pagina een aantal buttons geplaatst voor het bladeren en wisselen van view. Door te kijken hoe de buttons de functie aanroepen was mij al snel duidelijk dat dit de juiste manier is.
Kijk op de demo pagina om te zien dat het werkt DEMO Calendar
Hoe heb ik het nu opgelost? Door een dynamic action te maken die afschiet als het item met de LOV-popup wijzigt.
Deze Dynamic action heeft 2 true actions.
Page Items to Submit | P4_EMPNO,P4_CALENDAR_TYPE | |
Page Items to Return | P4_CALENDAR_DATE,P4_CALENDAR_DATE_WK |
Dit moet natuurlijk aangepast worden naar behoefte. In mijn geval wil ik dat de calendar springt naar de datum waarop de gekozen employee is aangenomen (HIREDATE). Dat ik 6 dagen van de datum afhaal indien het calendar type op Week (W) staat heeft te maken met hoe deze regio met de start datum en de eerste dag van de week omgaat. De regio laat in principe de eerste week na de datum zien. De standaard waarde van zondag voor de eerste dag van de week heb ik niet gewijzigd.
Deze actie doet de werkelijke refresh. De eerste parameter is om te zorgen dat de gekozen view (Maand,Week,List) gelijk blijft. Met de tweede parameter kan je de pagina naar de volgende (next) of vorige (previous) pagina laten verspringen. Hij hoeft voor ons niet te verspringen omdat wij hem openen op de juiste startdatum die in parameter 3 meegegeven wordt.
Dat is eigenlijk alles. Ik heb echter nog een extra dynamic action toegevoegd die de click van de buttons (Maand, week en list als je die actief hebt op je scherm zoals in mijn geval) afvangt en het event ‘change’ van de LOV-popup activeert. Op deze manier wordt ook na het wisselen van de view de pagina met de juiste datum getoond.
Dropdown menu in APEX
Een van de eerste eisen van een nieuwe APEX applicatie is natuurlijk een “mooi” menu. De eisen, wensen en voorkeuren van een menu willen nog wel eens verschillen. De standaard binnen apex zijn de TABS. Ik persoonlijk vind deze tabs erg lastig en bewerkelijk om te gebruiken. Het zou handig zijn als je een menu kan realiseren met alleen CSS en html. In mijn eigen zoektocht om een goed en degelijk menu te vinden stuitte ik op diversen mogelijkheden maar het beste uitgangspunt was een presentatie die ik ooit gezien heb. In deze oplossing wordt gebruik gemaakt van het CSS Dropdown Menu Framework gecombineerd met een HTML list item en template.
Kijk hier voor een voorbeeld http://apex.oracle.com/pls/apex/f?p=14108:1
Om met het menu te kunnen werken zal er een custom list template worden gemaakt.
Hier staat wat er in de diverse blokken van de template moet worden ingevuld.
Before List Entry |
List Template Before Rows |
<link href=”#IMAGE_PREFIX#themes/theme_200/menu/css/dropdown/dropdown.css” rel=”stylesheet” type=”text/css” /> <link href=”#IMAGE_PREFIX#themes/theme_200/menu/css/dropdown/themes/default/default.advanced.css” rel=”stylesheet” type=”text/css” /> <ul id=”nav” class=”dropdown dropdownhorizontal”> |
Template definition |
List Template Current |
<li><a href=”#LINK#” class=”currentTab”>#TEXT#</a></li> |
List Template Current with Sublist Items |
<li><a href=”#LINK#” class=”currentTab”>#TEXT#</a> |
List Template Noncurrent |
<li><a href=”#LINK#”>#TEXT#</a></li> |
List Template Noncurrent with Sublist Items |
<li><a href=”#LINK#”>#TEXT#</a |
Before Sublist Entry |
Sublist Template Before Rows |
<ul> |
Sublist Enry |
Sublist Template Current |
<li><a href=”#LINK#”>#TEXT#</a></li> |
Sublist Template Current with Sublist Items |
<li><a href=”#LINK#”>#TEXT# ></a> |
Sublist Template Noncurrent |
<li><a href=”#LINK#”>#TEXT#</a></li> |
Sublist Template Noncurrent with Sublist Items |
<li><a href=”#LINK#”>#TEXT# ></a> |
After Sublist Entry |
Sublist Template After Rows |
</ul></li> |
After List Entry |
List Template After Rows |
</li></ul> |
List Entry Current for Pages Tpe |
Exist SQL Query
|
List Entry Current for Condition (voorbeeld voor pagina 200) |
select null from dual where :app_page_id between 200 and 299 |
Hier komt de sidebar