Sie sind auf Seite 1von 138

a

allAPEX

And out of Chaos came the


Perfect APEX application

Alex Nuijten
a
allAPEX

And out of Chaos came the


Perfect APEX application

Alex Nuijten
MIKI Yoshihito https://flic.kr/p/EhKKKM
a
allAPEX

! @alexnuijten
nuijten.blogspot.com
500+ Technical
Experts Helping
Peers Globally

3 Membership Tiers Connect:


• Oracle ACE Director oracle-ace_ww@oracle.com
bit.ly/OracleACEProgram
• Oracle ACE
• Oracle ACE Associate Facebook.com/oracleaces
@oracleace

Nominate yourself or someone you know: acenomination.oracle.com


#malagAPEX18

a new yearly ā'pěks conference in the sun


Malaga, Spain 31st May – 1st June, 2018
malagAPEX.com
Jake Guild https://flic.kr/p/f1nDDQ
“With HTMLDB you can create applications so fast, …”

ODTUG Kaleidsoscope 2005, unknown presenter


“…you won't give the end user time to change their mind.”

ODTUG Kaleidsoscope 2005, unknown presenter



Easy
Beautiful Fast

g i n g
h a n
C
Cool User Friendly

L i f e
Crisp Scalable
Fun
APEXis
Easy
Too
FAIL
F A IL
irst ttempt n earning
KISS
K IS S
eep t imple tupid
KICKME
K IC K
eep t omplicated eeps

ME e mployed
#
it's all Dynamic SQL
New Requirement

We Ne e d

C us t ome r R at ing!

Do it !
alter table demo_customers

add (cust_rating number (1))

/


comment on column demo_customers.cust_rating

is

'How do we rate this customer? Score: 0-9'

/


update demo_customers

set cust_rating = 5

/


alter table demo_customers

modify cust_rating not null

/
alter table demo_customers

add (cust_rating number (1))

/


comment on column demo_customers.cust_rating

is

'How do we rate this customer? Score: 0-9'

/


update demo_customers

set cust_rating = 5

/


alter table demo_customers

modify cust_rating not null

/
alter table demo_customers

add (cust_rating number (1))

/


comment on column demo_customers.cust_rating

is

'How do we rate this customer? Score: 0-9'

/


update demo_customers

set cust_rating = 5

/


alter table demo_customers

modify cust_rating not null

/
alter table demo_customers

add (cust_rating number (1))

/


comment on column demo_customers.cust_rating

is

'How do we rate this customer? Score: 0-9'

/


update demo_customers

set cust_rating = 5

/


alter table demo_customers

modify cust_rating not null

/
$%
$
%
#
it's all Dynamic SQL
What
Would be
the Correct
Solution?

Photo by Sergio Rao on Unsplash


Concatenation Chaos
cust_last_name || ', ' || cust_first_name
cust_first_name || ', ' || cust_last_name
cust_first_name || ' ' || cust_last_name
select
search_title,
apex_util.prepare_url(search_link) search_link,
search_desc,
'Type' as label_01,
type as value_01,
null search_date
from (
select 1 display_seq,
customer_id id,
'Customer' type,
cust_last_name||', '||cust_first_name search_title,
cust_street_address1||' '||cust_street_address2||', '||cust_city||' '||cust_state||' '||
cust_postal_code search_desc,
'f?p='||:APP_ID||':7:'||:APP_SESSION||':::7:P7_CUSTOMER_ID,P7_BRANCH:'||
apex_escape.html(customer_id)||',30' search_link
from demo_customers
where ( instr(upper(cust_first_name),upper(:P30_SEARCH)) > 0
or instr(upper(cust_last_name),upper(:P30_SEARCH)) > 0
or instr(upper(cust_email),upper(:P30_SEARCH)) > 0
or instr(upper(cust_street_address1),upper(:P30_SEARCH)) > 0
or instr(upper(cust_street_address2),upper(:P30_SEARCH)) > 0
or instr(upper(cust_city),upper(:P30_SEARCH)) > 0
or instr(upper(cust_state),upper(:P30_SEARCH)) > 0
or instr(upper(cust_postal_code),upper(:P30_SEARCH)) > 0
or instr(upper(phone_number1),upper(:P30_SEARCH)) > 0
or instr(upper(phone_number2),upper(:P30_SEARCH)) > 0
or :P30_SEARCH is null )
and instr(:P30_OPTIONS,'C') > 0
union all
select 2 display_seq,
product_id id,
'Product' type,
product_name title,
category||' $'||list_price||' '
||( case when length(product_description) > 50 then
substr(product_description,1,50)||'...'
else
product_description
end ) detail,
'f?p='||:APP_ID||':6:'||:APP_SESSION||':::6:P6_PRODUCT_ID,P6_BRANCH:'||
apex_escape.html(product_id)||',30' search_link
from demo_product_info
where ( instr(upper(product_name),upper(:P30_SEARCH)) > 0
or instr(upper(product_description),upper(:P30_SEARCH)) > 0
or instr(upper(category),upper(:P30_SEARCH)) > 0
or instr(upper(list_price),upper(:P30_SEARCH)) > 0
or :P30_SEARCH is null )
and instr(:P30_OPTIONS,'P') > 0
union all
select distinct 3 display_seq,
o.order_id id,
'Order' type,
o.order_timestamp||' $'||o.order_total title,
c.cust_last_name||', '||c.cust_first_name detail,
'f?p='||:APP_ID||':29:'||:APP_SESSION||':SEARCH::29:P29_ORDER_ID,P29_LAST_PAGE:'||
apex_escape.html(o.order_id)||',30' search_link
from demo_orders o,
demo_customers c,
demo_order_items oi,
demo_product_info p
where o.customer_id = c.customer_id
and o.order_id = oi.order_id
and oi.product_id = p.product_id
and ( instr(upper(o.order_total),upper(:P30_SEARCH)) > 0
or instr(upper(o.order_timestamp),upper(:P30_SEARCH)) > 0
or instr(upper(o.order_total),upper(:P30_SEARCH)) > 0
or instr(upper(c.cust_first_name),upper(:P30_SEARCH)) > 0
or instr(upper(c.cust_last_name),upper(:P30_SEARCH)) > 0
or instr(upper(p.product_name),upper(:P30_SEARCH)) > 0
or instr(upper(p.product_description),upper(:P30_SEARCH)) > 0
or instr(upper(p.category),upper(:P30_SEARCH)) > 0
or :P30_SEARCH is null )
and instr(:P30_OPTIONS,'O') > 0
union all
select 4 display_seq,
customer_id id,
'Customer' type,
cust_last_name||', '||cust_first_name title,
cust_street_address1||' '||cust_street_address2||', '||cust_city||' '||cust_state||' '||
cust_postal_code detail,
'f?p='||:APP_ID||':7:'||:APP_SESSION||':::7:P7_CUSTOMER_ID,P7_BRANCH:'||
apex_escape.html(customer_id)||',30' search_link
from demo_customers
where instr(upper(tags),upper(:P30_SEARCH)) > 0
and instr(:P30_OPTIONS,'T') > 0
union all
select 4 display_seq,
product_id id,
'Product' type,
product_name title,
category||' $'||list_price||' '
||( case when length(product_description) > 50 then
substr(product_description,1,50)||'...'
else
product_description
end ) detail,
'f?p='||:APP_ID||':6:'||:APP_SESSION||':::6:P6_PRODUCT_ID,P6_BRANCH:'||
apex_escape.html(product_id)||',30' search_link
from demo_product_info
where instr(upper(tags),upper(:P30_SEARCH)) > 0
and instr(:P30_OPTIONS,'T') > 0
union all
select distinct 4 display_seq,
o.order_id id,
'Order' type,
o.order_timestamp||' $'||o.order_total title,
c.cust_last_name||', '||c.cust_first_name detail,
'f?p='||:APP_ID||':29:'||:APP_SESSION||':SEARCH::29:P29_ORDER_ID:'||apex_escape.html(o.order_id)
search_link
from demo_orders o,
demo_customers c,
demo_order_items oi,
demo_product_info p
where o.customer_id = c.customer_id
and o.order_id = oi.order_id
and oi.product_id = p.product_id
and instr(upper(o.tags),upper(:P30_SEARCH)) > 0
and instr(:P30_OPTIONS,'T') > 0
) order by display_seq, search_title
#
Impact Analysis is Tricky

Photo by Sergio Rao on Unsplash




] 10%

 ] 90%
#SmartDB
Views Packages

& & &


& &
Page Alias
Never Access Tables Directly
One or More Views per Page
Only Calls to Packages
Page Alias
 <APP_ALIAS><Page Nr>
Never Access Tables Directly
One or More Views per Page STRUCT1234
Only Calls to Packages
0 999 General Purpose

1000 6999 Main Application

7000 8999 Reporting

9000 Maintenance, Configuration


STRUCT1000

STRUCT1010 STRUCT1020

STRUCT1021 STRUCT1022 STRUCT1023


>= APEX 5

&APP_PAGE_ALIAS.
>= APEX 5
Page Alias
Never Access Tables Directly
One or More Views per Page
Only Calls to Packages
V_STRUCT1010 STRUCT1000 V_STRUCT1020

STRUCT1010 STRUCT1020
V_STRUCT1021

STRUCT1021 STRUCT1022 STRUCT1023

V_STRUCT1021_2
V_STRUCT1010 STRUCT1000 V_STRUCT1020

STRUCT1010 STRUCT1020
V_STRUCT1021

STRUCT1021 STRUCT1022 STRUCT1023

V_STRUCT1021_2

&
( ( ( ( ( ( ( (

( ( ( ( ( ( ( (

( ' ( ( ( ( ( (

( ( ( ( ' ( ( (

( ( ( ( ( ( ( (

( ( ( ' ( ( ( (

( ( ( ( ( ( ( (

( ( ( ( ( ( ( (
( ( (
Reports: Read Only Views
They're Easy

Simple Views

Regular Views They're Harder

Complex Views
They're Easy

Simple Views

Just follow the


With Check

Regular Views
Wizard
Option

Complex Views
Simple Views

Regular Views They're Harder

Complex Views

Replace DML
Processes
Simple Views

View

&
Complex Views

Views Packages

& & &


& &
Complex Views

Views Packages

& & &


& &
Lost Update
Scott
Deptno: 20
10

& Scott
Deptno: 20
10
Scott James
Scott
Deptno: 20
10 Deptno: 10

Up d ate is L o s t ! James
Scott
&
Deptno: 10
20
Scott James
Scott
Deptno: 20
10 Deptno: 10
MD5: AB42 MD5: AB42

( '

Scott
&
Deptno: 20
10 MD5: CF65
AB42
-- build MD5 function for table "DEMO_CUSTOMERS"

function "BUILD_DEMO_CUSTOMERS_MD5" (

"P_CUSTOMER_ID" in number,... 

) return varchar2 is 

begin

return apex_util.get_hash(apex_t_varchar2(

"P_CUST_FIRST_NAME",

"P_CUST_LAST_NAME",

"P_CUST_STREET_ADDRESS1",

"P_CUST_STREET_ADDRESS2",

"P_CUST_CITY",

"P_CUST_STATE",

"P_CUST_POSTAL_CODE",

"P_CUST_EMAIL",

"P_PHONE_NUMBER1",

"P_PHONE_NUMBER2",

"P_URL",

"P_CREDIT_LIMIT",

"P_TAGS",

"P_CUST_RATING" ));

end "BUILD_DEMO_CUSTOMERS_MD5";
-- build MD5 function for table "DEMO_CUSTOMERS"

function "BUILD_DEMO_CUSTOMERS_MD5" (

"P_CUSTOMER_ID" in number,... 

) return varchar2 is 

begin

return apex_util.get_hash(apex_t_varchar2(

"P_CUST_FIRST_NAME",

"P_CUST_LAST_NAME",

"P_CUST_STREET_ADDRESS1",

"P_CUST_STREET_ADDRESS2",

"P_CUST_CITY",

"P_CUST_STATE",

"P_CUST_POSTAL_CODE",

"P_CUST_EMAIL",

"P_PHONE_NUMBER1",

"P_PHONE_NUMBER2",

"P_URL",

"P_CREDIT_LIMIT",

"P_TAGS",

"P_CUST_RATING" ));

end "BUILD_DEMO_CUSTOMERS_MD5";
-- update procedure for table "DEMO_CUSTOMERS"

procedure "UPD_DEMO_CUSTOMERS" (

"P_CUSTOMER_ID" in number,...

"P_MD5" in varchar2 default null)

is 

"L_MD5" varchar2(32767) := null;

begin

if "P_MD5" is not null then

for c1 in (

select * from "DEMO_CUSTOMERS" 

where "CUSTOMER_ID" = "P_CUSTOMER_ID" FOR UPDATE

) loop

"L_MD5" := "BUILD_DEMO_CUSTOMERS_MD5"(

c1."CUSTOMER_ID", ... );

end loop;

end if;

if ("P_MD5" is null) or ("L_MD5" = "P_MD5") then 

update "DEMO_CUSTOMERS"

set "CUSTOMER_ID" = "P_CUSTOMER_ID"

...

where "CUSTOMER_ID" = "P_CUSTOMER_ID";

else

raise_application_error (-20001,'Current version of data in database has changed since user
initiated update process. current checksum = "'||"L_MD5"||'", item checksum = "'||"P_MD5"||'".'); 

end if;

end "UPD_DEMO_CUSTOMERS";
-- update procedure for table "DEMO_CUSTOMERS"

procedure "UPD_DEMO_CUSTOMERS" (

"P_CUSTOMER_ID" in number,...

"P_MD5" in varchar2 default null)

is 

"L_MD5" varchar2(32767) := null;

begin

if "P_MD5" is not null then

for c1 in (

select * from "DEMO_CUSTOMERS" 

where "CUSTOMER_ID" = "P_CUSTOMER_ID" FOR UPDATE

) loop

"L_MD5" := "BUILD_DEMO_CUSTOMERS_MD5"(

c1."CUSTOMER_ID", ... );

end loop;

end if;

if ("P_MD5" is null) or ("L_MD5" = "P_MD5") then 

update "DEMO_CUSTOMERS"

set "CUSTOMER_ID" = "P_CUSTOMER_ID"

...

where "CUSTOMER_ID" = "P_CUSTOMER_ID";

else

raise_application_error (-20001,'Current version of data in database has changed since user
initiated update process. current checksum = "'||"L_MD5"||'", item checksum = "'||"P_MD5"||'".'); 

end if;

end "UPD_DEMO_CUSTOMERS";
-- update procedure for table "DEMO_CUSTOMERS"

procedure "UPD_DEMO_CUSTOMERS" (

"P_CUSTOMER_ID" in number,...

"P_MD5" in varchar2 default null)

is 

"L_MD5" varchar2(32767) := null;

begin

if "P_MD5" is not null then

for c1 in (

select * from "DEMO_CUSTOMERS" 

where "CUSTOMER_ID" = "P_CUSTOMER_ID" FOR UPDATE

) loop

"L_MD5" := "BUILD_DEMO_CUSTOMERS_MD5"(

c1."CUSTOMER_ID", ... );

end loop;

end if;

if ("P_MD5" is null) or ("L_MD5" = "P_MD5") then 

update "DEMO_CUSTOMERS"

set "CUSTOMER_ID" = "P_CUSTOMER_ID"

...

where "CUSTOMER_ID" = "P_CUSTOMER_ID";

else

raise_application_error (-20001,'Current version of data in database has changed since user
initiated update process. current checksum = "'||"L_MD5"||'", item checksum = "'||"P_MD5"||'".'); 

end if;

end "UPD_DEMO_CUSTOMERS";
-- update procedure for table "DEMO_CUSTOMERS"

procedure "UPD_DEMO_CUSTOMERS" (

"P_CUSTOMER_ID" in number,...

"P_MD5" in varchar2 default null)

is 

"L_MD5" varchar2(32767) := null;

begin

if "P_MD5" is not null then

for c1 in (

select * from "DEMO_CUSTOMERS" 

where "CUSTOMER_ID" = "P_CUSTOMER_ID" FOR UPDATE

) loop

"L_MD5" := "BUILD_DEMO_CUSTOMERS_MD5"(

c1."CUSTOMER_ID", ... );

end loop;

end if;

if ("P_MD5" is null) or ("L_MD5" = "P_MD5") then 

update "DEMO_CUSTOMERS"

set "CUSTOMER_ID" = "P_CUSTOMER_ID"

...

where "CUSTOMER_ID" = "P_CUSTOMER_ID";

else

raise_application_error (-20001,'Current version of data in database has changed since user
initiated update process. current checksum = "'||"L_MD5"||'", item checksum = "'||"P_MD5"||'".'); 

end if;

end "UPD_DEMO_CUSTOMERS";
-- update procedure for table "DEMO_CUSTOMERS"

procedure "UPD_DEMO_CUSTOMERS" (

"P_CUSTOMER_ID" in number,...

"P_MD5" in varchar2 default null)

is 

"L_MD5" varchar2(32767) := null;

begin

if "P_MD5" is not null then

for c1 in (

select * from "DEMO_CUSTOMERS" 

where "CUSTOMER_ID" = "P_CUSTOMER_ID" FOR UPDATE

) loop

"L_MD5" := "BUILD_DEMO_CUSTOMERS_MD5"(

c1."CUSTOMER_ID", ... );

end loop;

end if;

if ("P_MD5" is null) or ("L_MD5" = "P_MD5") then 

update "DEMO_CUSTOMERS"

set "CUSTOMER_ID" = "P_CUSTOMER_ID"

...

where "CUSTOMER_ID" = "P_CUSTOMER_ID";

else

raise_application_error (-20001,'Current version of data in database has changed since user
initiated update process. current checksum = "'||"L_MD5"||'", item checksum = "'||"P_MD5"||'".'); 

end if;

end "UPD_DEMO_CUSTOMERS";
Page Alias
Never Access Tables Directly
One or More Views per Page
Only Calls to Packages
obvious
API: clearly defined methods
of communication between various
software components.
STRUCT1000

STRUCT1010 STRUCT1020

STRUCT_1010_PKG
STRUCT1021 STRUCT1022 STRUCT1023
MD5 DEL
INS UPD
STRUCT_1021_PKG

MD5 Reimburse
Process Create

Validate
STRUCT1021

STRUCT_1021_PKG

MD5 Reimburse
Process Create

Customer API Order API Invoice API

& & & & &


STRUCT1021

STRUCT_1021_PKG

MD5 Reimburse
Process Create

Customer API Order API Invoice API

& & & & &


STRUCT1021 STRUCT2011

STRUCT_1021_PKG STRUCT_2011_PKG

MD5 Reimburse
Process Create
Validate

Customer API Order API Invoice API

& & & & &


of course fully instrumented
Logger

iangbl https://flic.kr/p/vSwhv
) http://www.oraopensource.com/
Virtual Column

Photo by Micheile Henderson on Unsplash


Column Based
on Expression

Photo by Micheile Henderson on Unsplash


alter table demo_customers

add customer_name generated always 

as (cust_last_name || ', ' || cust_first_name)
alter table demo_customers

add customer_name generated always 

as (cust_last_name || ', ' || cust_first_name)
alter table demo_customers

add customer_address generated always as 

(cust_street_address1 || 

decode(cust_street_address2, null, null, ', ' ||

cust_street_address2))
alter table demo_customers

add customer_address generated always as 

(cust_street_address1 || 

decode(cust_street_address2, null, null, ', ' ||

cust_street_address2))
create or replace view v_cust0002

as

select customer_id, 

customer_name, 

customer_address, 

cust_city, 

cust_state, 

cust_postal_code,

tags

from demo_customers
create or replace view v_cust0002

as

select customer_id, 

customer_name, 

customer_address, 

cust_city, 

cust_state, 

cust_postal_code,

tags

from demo_customers
process_customer (p_customer_id in demo_customers.customer_id%type

,p_cust_first_name in demo_customers.cust_first_name%type

,p_cust_last_name in demo_customers.cust_last_name%type

,p_cust_street_address1 in demo_customers.cust_street_address1%type

,p_cust_street_address2 in demo_customers.cust_street_address2%type

,p_cust_city in demo_customers.cust_city%type

,p_cust_state in demo_customers.cust_state%type

,p_cust_postal_code in demo_customers.cust_postal_code%type

,p_tags in demo_customers.tags%type

);
process_customer (p_customer_id in demo_customers.customer_id%type

,p_cust_first_name in demo_customers.cust_first_name%type

,p_cust_last_name in demo_customers.cust_last_name%type

,p_cust_street_address1 in demo_customers.cust_street_address1%type

,p_cust_street_address2 in demo_customers.cust_street_address2%type

,p_cust_city in demo_customers.cust_city%type

,p_cust_state in demo_customers.cust_state%type

,p_cust_postal_code in demo_customers.cust_postal_code%type

,p_tags in demo_customers.tags%type

);
process_customer (p_customer_id in demo_customers.customer_id%type

,p_cust_first_name in demo_customers.cust_first_name%type

,p_cust_last_name in demo_customers.cust_last_name%type

,p_cust_street_address1 in demo_customers.cust_street_address1%type

,p_cust_street_address2 in demo_customers.cust_street_address2%type

,p_cust_city in demo_customers.cust_city%type

,p_cust_state in demo_customers.cust_state%type

,p_cust_postal_code in demo_customers.cust_postal_code%type

,p_tags in demo_customers.tags%type

);
Maybe Hide the
Columns from
Developer
alter table demo_customers

modify cust_first_name

invisible

Photo by Milivoj Kuhar on Unsplash


Maybe Not

Photo by Milivoj Kuhar on Unsplash


https://flic.kr/p/ZRNNZq
Steven Lam
Datamodel
SmartDB
APEX
a
allAPEX

?#
Alex Nuijten

+ www.allAPEX.nl , alex@allAPEX.nl ! @alexnuijten - nuijten.blogspot.com


%
$
Frank Boston Dennis Yang
https://flic.kr/p/5SJBvA https://flic.kr/p/2G3HMo

Das könnte Ihnen auch gefallen