Oplossing voor ORA-02050 en ORA-02051

Onlangs liep ik tegen een vervelend probleem aan: een in de ontwikkel- en testomgeving prima draaiend batchprogramma gaf in de acceptatieomgeving opeens allerlei foutmeldingen.

Wat was er aan de hand? Een aanzienlijke stored procedure (package) in een Oracle 11g database had tot doel data van een tweetal databases te converteren naar een derde. Dit gebeurde in een grote loop, met allerlei cursoren waarbij in 75% van de gevallen met databaselinks werd gewerkt. Na elke doorloop van de buitenste loop werd een commit uitgevoerd. Hier ging het mis: na de commit trad een foutmelding op: ORA-02050 (transaction string rolled back, some remote DBs may be in-doubt) gevolgd door ORA-02051 (another session in same transaction failed). De integriteit van de database die over de databaselink wordt benaderd ten opzicht van de opgehaalde dataset wordt betwijfeld en Oracle draait de hele transactie terug. Verschil tussen ontwikkel en test aan de ene kant en acceptatie was de hoeveelheid data-activiteit. In acceptatie waren behoorlijk veel transacties in de gelinkte databases, in ontwikkel en test vrijwel geen.

Belangrijk om te weten is dat Oracle ook bij het uitsluitend opvragen van data via een databaselink toch een transactie start. Zolang er geen commit wordt gegeven, blijven de opgevraagde rijen dus in de sessie gereserveerd.

De oplossing voor het probleem is derhalve, om direct na het opvragen van de rij(en) via een databaselink een commit uit te voeren. Meestal is het echter in een grote loop, waarin allerlei controles dienen te worden uitgevoerd niet wenselijk om tussentijdse commits uit te voeren. Vandaar dat ik er voor gekozen heb om alle cursoren te vervangen door functies, die via een autonomous_transaction pragma een aparte sessie starten om de data op te halen. Deze kan vervolgens afgesloten worden met een commit en het probleem is verholpen.

Een voorbeeld:

Oude situatie

[code type=”sql”]
declare

cursor c_tst(b_parameter in varchar2)
is
select *
from tst_tabel
where code = b_parameter;

begin

for r_tst in c_tst(‘FOO’) loop

end loop;

commit;
end;
[/code]

Nieuwe situatie

[code type=”sql”]
declare
type t_tst is table of tst_tabel%rowtype index by binary_integer;
l_tst t_tst;

function get_tst(p_parameter)
return t_tst
is
pragma autonomous_transaction;
l_tst t_tst;

cursor c_tst(b_parameter in varchar2)
is
select *
from tst_tabel
where code = b_parameter;

begin
open c_tst(p_parameter);
fetch c_tst bulk collect into l_tst;
close c_tst;
commit;
return l_tst;
end get_tst;

begin
l_tst := get_tst(‘FOO’);
for i in 1..l_tst.count loop

end loop;
commit;
end;
[/code]

Beetje meer code, maar wel opgelost.

Kalender deel 2: Werkdagen

Oracle SQL biedt standaard functionaliteit voor het berekenen van het aantal dagen of het aantal maanden tussen twee datums. Bijvoorbeeld:

SQL> select to_date('16112013','ddmmyyyy') - to_date('06052013','ddmmyyyy') "days" from dual;

days
----------
194

SQL> select months_between(to_date('16112013','ddmmyyyy'), to_date('16052013','ddmmyyyy')) "months" from dual;

months
----------
6

Ook het optellen van dagen of maanden bij een datum is eenvoudig met SQL te doen.

SQL> select to_date('06052013','ddmmyyyy') + 23 "date" from dual;

date
----------
29-05-2013

SQL> select add_months(to_date('06052013','ddmmyyyy'),3) "date" from dual;

date
----------
06-08-2013

Daarentegen is voor het bepalen van het aantal werkdagen tussen twee datums of het optellen van werkdagen bij een datum niet standaard in SQL voorzien. Dus zelf maar iets fabriceren. Het doel is de volgende twee functies:

 

Dagnummers

Voor de flexibiliteit heb ik aan de functies de parameter p_no_business toegevoegd. Hiermee wordt aangegeven welke dagen GEEN werkdagen zijn. De parameter kan combinaties van de volgende dagnummers bevatten:

1 = maandag
2 = dinsdag
3 = woensdag
4 = donderdag
5 = vrijdag
6 = zaterdag
7 = zondag
0 = feestdag

De default waarde van p_no_business is “067”. In dit geval worden de zater-, zon- en feestdagen niet gekenmerkt als een werkdag.

De voor de hand liggende manier om de dagnummer van een datum te achterhalen is met: to_char(p_date,'d'). Deze wijze is echter afhankelijk van de Oracle parameter NLS_TERRITORY. Ik gebruik daarom liever de formule: trunc(sysdate) - trunc(sysdate,'IW'). Onderstaande query maakt dit duidelijk.

SQL> alter session set nls_territory = 'AMERICA';

SQL> select value nls_territory
, to_char(sysdate,'day') day
, to_char(sysdate,'d') no_1
, trunc(sysdate) - trunc(sysdate,'IW') + 1 no_2
from   v$nls_parameters
where  parameter = 'NLS_TERRITORY';

SQL> alter session set nls_territory = 'THE NETHERLANDS';

SQL> select value nls_territory
, to_char(sysdate,'day') day
, to_char(sysdate,'d') no_1
, trunc(sysdate) - trunc(sysdate,'IW') + 1 no_2
from   v$nls_parameters
where  parameter = 'NLS_TERRITORY';

NLS_TERRITORY   DAY     NO_1 NO_2
--------------- ------- ---- ----
AMERICA         maandag    2    1

NLS_TERRITORY   DAY     NO_1 NO_2
--------------- ------- ---- ----
THE NETHERLANDS maandag    1    1

 

Feestdagen

Indien parameter p_no_business “0” bevat, is het van belang de feestdagen te kunnen bepalen. Dit staat beschreven in mijn vorige blog; wat resulteert in de volgende code.

create or replace package calendar
/**********************
* 2013 www.orcado.nl *
**********************/
is
type holiday_rec_typ is record(holiday_name varchar2(25)
,holiday_date date);
type holiday_tab_typ is table of holiday_rec_typ;
end calendar;
/

create or replace function holidays
(p_year in pls_integer)
return calendar.holiday_tab_typ pipelined
/**********************
* 2013 www.orcado.nl *
**********************/
is
l_easter_sunday date := easter_sunday(p_year);
l_kingsday date := kingsday(p_year);
function set
(p_name in varchar2
,p_date in date)
return calendar.holiday_rec_typ
is
l_rec calendar.holiday_rec_typ;
begin
l_rec.holiday_name := p_name;
l_rec.holiday_date := p_date;
return l_rec;
end set;
begin
if p_year > 1952 then
pipe row(set('Nieuwjaarsdag',to_date('0101'||p_year,'ddmmyyyy')));
pipe row(set('1e Kerstdag',to_date('2512'||p_year,'ddmmyyyy')));
pipe row(set('2e Kerstdag',to_date('2612'||p_year,'ddmmyyyy')));
end if;
if p_year > 1989 then
pipe row(set('Bevrijdingsdag',to_date('0505'||p_year,'ddmmyyyy')));
end if;
if l_kingsday is not null then
pipe row(set('Koning' || (case when p_year < 2014 then 'inne' else 's' end) || 'dag',l_kingsday));
end if;
if l_easter_sunday is not null then
pipe row(set('Goede vrijdag',l_easter_sunday - 2));
pipe row(set('1e Paasdag',l_easter_sunday));
pipe row(set('2e Paasdag',l_easter_sunday + 1));
pipe row(set('Hemelvaartsdag',l_easter_sunday + 39));
pipe row(set('1e Pinksterdag',l_easter_sunday + 49));
pipe row(set('2e Pinksterdag',l_easter_sunday + 50));
end if;
return;
end holidays;
/

SQL> select holiday_name "Name"
, to_char(holiday_date,'dy dd-mm-yyyy') "Date"
from table(holidays(2013))
order by holiday_date;

Name            Date
--------------- -------------
Nieuwjaarsdag   di 01-01-2013
Goede vrijdag   vr 29-03-2013
1e Paasdag      zo 31-03-2013
2e Paasdag      ma 01-04-2013
Koninginnedag   di 30-04-2013
Bevrijdingsdag  zo 05-05-2013
Hemelvaartsdag  do 09-05-2013
1e Pinksterdag  zo 19-05-2013
2e Pinksterdag  ma 20-05-2013
1e Kerstdag     wo 25-12-2013
2e Kerstdag     do 26-12-2013

 

check_business_day

Het voorgaande over dagnummers en feestdagen verwerk ik in een functie, check_business_day, die kan achterhalen of een gegeven dag een werkdag is.

create or replace function check_business_day
(p_date        in date
,p_no_business in varchar2 := '067')
return boolean
/**********************
* 2013 www.orcado.nl *
**********************/
is
l_day_no pls_integer := trunc(p_date) - trunc(p_date,'IW') + 1;
l_dummy  pls_integer;
begin
if instr(p_no_business,l_day_no) > 0 then
return false;
end if;
if instr(p_no_business,'0') > 0 then
begin
select 1
into   l_dummy
from   table(holidays(to_char(p_date,'yyyy')))
where  trunc(holiday_date) = trunc(p_date);
return false;
exception
when others then
null;
end;
end if;
return true;
end check_business_day;
/

SQL> set serveroutput on

SQL> declare
procedure p
(p_date in varchar2
,p_no_business in varchar2)
is
l_date date := to_date(p_date,'ddmmyyyy');
begin
dbms_output.put_line(to_char(l_date,'dy dd-mm-yyyy')
|| ' '
|| rpad(p_no_business,13)
|| (case when check_business_day(l_date,p_no_business)
then '+'
else '-'
end));
end p;
begin
dbms_output.put_line('Date No business Business day');
dbms_output.put_line('-------------- ------------ ------------');
p('09052013','067');
p('10052013','067');
p('11052013','067');
p('12052013','067');
p('09052013','07');
p('10052013','07');
p('11052013','07');
p('12052013','07');
p('09052013','7');
p('10052013','7');
p('11052013','7');
p('12052013','7');
end;
/

Date           No business  Business day
-------------- ------------ ------------
do 09-05-2013  067          -
vr 10-05-2013  067          +
za 11-05-2013  067          -
zo 12-05-2013  067          -
do 09-05-2013  07           -
vr 10-05-2013  07           +
za 11-05-2013  07           +
zo 12-05-2013  07           -
do 09-05-2013  7            +
vr 10-05-2013  7            +
za 11-05-2013  7            +
zo 12-05-2013  7            -

 

business_days_between

Een eenvoudige manier om nu het aantal werkdagen tussen twee datums te berekenen is gebruik te maken van een PL/SQL-loop en de functie check_business_day.

create or replace function business_days_between
(p_start_date  in date
,p_end_date    in date
,p_no_business in varchar2 := '067')
return pls_integer
/**********************
* 2013 www.orcado.nl *
**********************/
is
j pls_integer := 0;
begin
for i in 0..(trunc(p_end_date) - trunc(p_start_date)) loop
if check_business_day(p_start_date + i,p_no_business) then
j := j + 1;
else
if i = 0 then
j := 1;
end if;
end if;
end loop;
return j - 1;
end business_days_between;
/

SQL> def p_date = "13052013"

SQL> select to_char(to_date('&p_date','ddmmyyyy'),'dy dd-mm-yyyy') "start date"
, to_char(to_date('&p_date','ddmmyyyy') + level - 1,'dy dd-mm-yyyy') "end date"
, business_days_between(to_date('&p_date','ddmmyyyy')
,to_date('&p_date','ddmmyyyy') + level - 1
,'67') "business days"
from dual
connect by level <= 20;

start date    end date      business days
------------- ------------- -------------
ma 13-05-2013 ma 13-05-2013             0
ma 13-05-2013 di 14-05-2013             1
ma 13-05-2013 wo 15-05-2013             2
ma 13-05-2013 do 16-05-2013             3
ma 13-05-2013 vr 17-05-2013             4
ma 13-05-2013 za 18-05-2013             4
ma 13-05-2013 zo 19-05-2013             4
ma 13-05-2013 ma 20-05-2013             5
ma 13-05-2013 di 21-05-2013             6
ma 13-05-2013 wo 22-05-2013             7
ma 13-05-2013 do 23-05-2013             8
ma 13-05-2013 vr 24-05-2013             9
ma 13-05-2013 za 25-05-2013             9
ma 13-05-2013 zo 26-05-2013             9
ma 13-05-2013 ma 27-05-2013            10
ma 13-05-2013 di 28-05-2013            11
ma 13-05-2013 wo 29-05-2013            12
ma 13-05-2013 do 30-05-2013            13
ma 13-05-2013 vr 31-05-2013            14
ma 13-05-2013 za 01-06-2013            14

 

add_business_days

Op een vergelijkbare wijze is het ook eenvoudig een aantal werkdagen bij een datum op te tellen. Indien het aantal werkdagen is 0, dan wordt de eerst volgende werkdag geretourneerd.

create or replace function add_business_days
(p_date        in date
,p_number      in pls_integer
,p_no_business in varchar2 := '067')
return date
/**********************
* 2013 www.orcado.nl *
**********************/
is
i pls_integer := 0;
j pls_integer := 0;
begin
if p_number = 0 then
while not check_business_day(p_date + i,p_no_business) loop
i := i + 1;
end loop;
else
while j < abs(p_number) loop
i := i + sign(p_number);
if check_business_day(p_date + i,p_no_business) then
j := j + 1;
end if;
end loop;
end if;
return p_date + i;
end add_business_days;
/

SQL> def p_date = "13052013"

SQL> select to_char(to_date('&p_date','ddmmyyyy'),'dy dd-mm-yyyy') "start date"
, level - 1 "days"
, to_char(add_business_days(to_date('&p_date','ddmmyyyy')
,level - 1
,'67'),'dy dd-mm-yyyy') "end date"
from dual
connect by level <= 20;

start date          days end date
------------- ---------- -------------
ma 13-05-2013          0 ma 13-05-2013
ma 13-05-2013          1 di 14-05-2013
ma 13-05-2013          2 wo 15-05-2013
ma 13-05-2013          3 do 16-05-2013
ma 13-05-2013          4 vr 17-05-2013
ma 13-05-2013          5 ma 20-05-2013
ma 13-05-2013          6 di 21-05-2013
ma 13-05-2013          7 wo 22-05-2013
ma 13-05-2013          8 do 23-05-2013
ma 13-05-2013          9 vr 24-05-2013
ma 13-05-2013         10 ma 27-05-2013
ma 13-05-2013         11 di 28-05-2013
ma 13-05-2013         12 wo 29-05-2013
ma 13-05-2013         13 do 30-05-2013
ma 13-05-2013         14 vr 31-05-2013
ma 13-05-2013         15 ma 03-06-2013
ma 13-05-2013         16 di 04-06-2013
ma 13-05-2013         17 wo 05-06-2013
ma 13-05-2013         18 do 06-06-2013
ma 13-05-2013         19 vr 07-06-2013

Kan dit nog een keer gebeuren?

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.

Kalender deel 1: Erkende feestdagen

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 endpoints

Bij 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_calendar

Pas 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.

1) PL/SQL

declare
cursor c_date is
select HIREDATE
, case when :P4_CALENDAR_TYPE = ‘W’
then HIREDATE-6
else HIREDATE
end
from EMP
where empno = :P4_EMPNO;
begin
open c_date;
fetch c_date into :P4_CALENDAR_DATE,:P4_CALENDAR_DATE_WK;
close c_date;
end;
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.

2) Javascript

apex.widget.calendar.ajax_calendar($v(‘P4_CALENDAR_TYPE’),’same’,$v(‘P4_CALENDAR_DATE_WK’));

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.

3) Extra

apex.event.trigger(‘#P4_EMPNO’,’change’);

 

Hier komt de sidebar

Volg ons op

© Orcado B.V. | 1999 - 2013