Wednesday 29 January 2020

Unpivoting Oracle APEX meta-data

There are APEX dictionary views for most of the data that represents the 'source' that is your APEX application meta-data.

Note: This post has beeen updated to reflect me not looking very hard, but I added a performance comparison... just because.

Today I found one place where I really wanted to run a query to find references to potential data - application substitution strings.

APEX Application Substitution Strings

And yes, I have a CSS rule to right-align those names, to make it easier to read.

I couldn't find any references in the APEX dictionary (correction, see below), so I looked in the Oracle data dictionary to find where it may live.
select * from all_tab_columns 
where column_name like 'SUB%03';
So not only are these stored in a place inaccessible to us mere-mortal developers, they are also stored in 20 sets of name/value columns - not rows.

This means if you have a dozen applications, with references in slightly different locations for each application, then string searches might be a pain.

Unpivot to the rescue!
select * from (
  select id app_id, alias, name -- key facts
      -- all the substitution stringz!
      ,substitution_string_01, substitution_value_01
      ,substitution_string_02, substitution_value_02
      ,substitution_string_03, substitution_value_03
      ,substitution_string_04, substitution_value_04
      ,substitution_string_05, substitution_value_05
      ,substitution_string_06, substitution_value_06
      ,substitution_string_07, substitution_value_07
      ,substitution_string_08, substitution_value_08
      ,substitution_string_09, substitution_value_09
      ,substitution_string_10, substitution_value_10
      ,substitution_string_11, substitution_value_11
      ,substitution_string_12, substitution_value_12
      ,substitution_string_13, substitution_value_13
      ,substitution_string_14, substitution_value_14
      ,substitution_string_15, substitution_value_15
      ,substitution_string_16, substitution_value_16
      ,substitution_string_17, substitution_value_17
      ,substitution_string_18, substitution_value_18
      ,substitution_string_19, substitution_value_19
      ,substitution_string_20, substitution_value_20
  from apex_190200.WWV_FLOWS -- direct from underlying view
unpivot (
(str, val) -- new columns
for rec in -- denoted by 
  ((substitution_string_01, substitution_value_01) as '01'
  ,(substitution_string_02, substitution_value_02) as '02'
  ,(substitution_string_03, substitution_value_03) as '03'
  ,(substitution_string_04, substitution_value_04) as '04'
  ,(substitution_string_05, substitution_value_05) as '05'
  ,(substitution_string_06, substitution_value_06) as '06'
  ,(substitution_string_07, substitution_value_07) as '07'
  ,(substitution_string_08, substitution_value_08) as '08'
  ,(substitution_string_09, substitution_value_09) as '09'
  ,(substitution_string_10, substitution_value_10) as '10'
  ,(substitution_string_11, substitution_value_11) as '11'
  ,(substitution_string_12, substitution_value_12) as '12'
  ,(substitution_string_13, substitution_value_13) as '13'
  ,(substitution_string_14, substitution_value_14) as '14'
  ,(substitution_string_15, substitution_value_15) as '15'
  ,(substitution_string_16, substitution_value_16) as '16'
  ,(substitution_string_17, substitution_value_17) as '17'
  ,(substitution_string_18, substitution_value_18) as '18'
  ,(substitution_string_19, substitution_value_19) as '19'
  ,(substitution_string_20, substitution_value_20) as '20'
order by app_id, str 
This query transposes all the string/value columns into rows, each denonimated by the new "REC" column.

Note, this query can only be executed by those with the APEX Administrator Role, and if you want to have it within a view that could be executed by other schemas, then select access on wwv_flows is needed with grant option.

Columns unpivoted into rows

By placing the query in a view, I can now make queries like this to find any substitution strings in any applications that have date references.
select * from apx_app_sub_strings
where val like 'APP_DATE%';
Pretty neat, well at least as far as what I was trying to do.

This query may break in future versions, as it's based on an underlying, undocumented view.
I also think that once upon a time, there were fewer pairs.

Update: As the community quickly pointed out, I missed & forgot about a view already dedicated for such a task. I was too busy looking for a column number, when really I should have used APEX_APPLICATION_SUBSTITUTIONS.

I thought I'd see how 'they' solved the problem, and I was a little surprised to see a bunch of UNION statements.
      from wwv_flow_authorized auth,
           wwv_flows f
     where = auth.application_id )
select workspace,
       substitution_string_01 as substitution_string,
       substitution_value_01  as substitution_value
  from substitution
where substitution_string_01 is not null
union all
select workspace,
       substitution_string_02 as substitution_string,
       substitution_value_02  as substitution_value
  from substitution
where substitution_string_02 is not null
union all...
After seeing this I couldn't help but run a brute for comparison, running both solutions x times.
16.55 secs (.003310 secs per iteration) -- union all
 6.54 secs (.001308 secs per iteration) -- unpivot
I made sure the underlying join was the same, and I'm not all that surprised the unpivot did the job quicker.

Update 2: It appears 20.1 has gone with unpivot.

Thursday 16 January 2020

Performance on Classic Reports with LOVs

Just an observation made yesterday that I thougt was worth everone's consideration.

I created a classic report on a reference table with a touch over 1000 rows, with pagination set at 150 rows per page.

That’s all I had on the page.

The table had it's own set of lookups for a couple of columns, so I assigned two LOVs I already defined in Shared Components.

Classic Report LOV

Next minute, I had a simple reference page taking 10 seconds to load.

This is information from apex_workspace_activity_logs, showing results where no LOV was applied, two LOVs, and just one. I later found the number of rows shown in the pagination set also varied the result.

As I removed one of the LOVs, I quickly realised this was the problem.

I ran the page in debug mode, to see if something crazy was happening as it constructed the query.
When I used debug = YES, it pinned all the effort onto the one line item - but not the query itself.
rows loop: 150 row(s)

Looking again using LEVEL9, I could see that every row took a bit of work, not just the query.
In fact each row had three debug line items referencing my LOV lookup SQL.
begin begin SELECT name display_value, id return_value  
bulk collect into wwv_flow_utilities.g_display,wwv_flow_utilities.g_value 
FROM  my_secondary_ref 
ORDER by 1
; end;
Debug chart

Apparently these are only melded into the SQL for IR/IG, not Classic Reports. Context switching kills the page instead - well, at least 149 extra executions of the one statement.

I ran another test so I could check v$sqlarea, and sure enough, there are far more executions of this lookup tha necessary - unnecessarily churning the CPU.

v$sql_area status

I’ve tended to embed these in my queries anyway, often as some form of scalar subqery.
This is a habit I started as a Oracle Forms developer, since Post-Query lookups made repeated network calls that just slowed the application down.
select sm.*
  ,(select name
    from my_lookup m
    where = my_desc
from some_model sm
This makes me really wonder how much of our existing page generation time is spent on this work?
Even on small classic report regions. This all adds up.
If only there was a way I could find all occurences of LOVs used in classic reports... wait a minute! I can query the APEX dictionary! ;p
select application_id, pagE_id, page_name, region_name
  ,(select maximum_rows_to_query 
    from apex_application_page_regions r
    where r.region_id = c.region_id) nbr_rows
  , column_alias, heading 
  , named_list_of_values, inline_list_of_values
  , column_is_hidden, condition_type
  ,build_option, column_comment
from apex_application_page_rpt_cols c
where application_id = 102
and display_as_code = 'TEXT_FROM_LOV'
order by nbr_rows desc
It's a shame such a nify declarative feature impacts performance this way.

Wednesday 15 January 2020

So you've submitted an abstract

You've thought hard about your conference talk ideas; you then fleshed out your idea and worked hard on an abstract; and finally you plucked up the courage to make the submission to that big conference.

What next?

Keep momentum.


It really depends on the style of your talk, but in most cases, just keep whatever momentum you have (or had back in December), and work on it in some form every week.

Wait, abstracts closed. Public voting complete. Aren't I just waiting for a March notification before I bother starting?


There are a few reasons you could be working on your talks each week. For instance:
  1. Rome wasn't built in a day - it depends, but a reasonable talk should take at least 20 hours to develop, plus the time needed to gather the experience the talk represents, past, present, or future. Presentations left to the last minute can look & sound like it. Presentations started early will have time to cultivate, allow your ideas to progress as you write up progress so
  2. Experience - even if your talk isn't accepted, you have gained the experience necessary for a topic you've considered worthy enough to talk about it. Maybe you've already gained the experience, but compiling your thoughts will help you gain a deeper understanding of the topic.
    Maybe you gathered a few important insights along the way.
  3. Aim high - OK, maybe you end up getting a "sorry, not this time" email for the big conference. So what's happening locally? Do you have a regular meetup nearby? Is there another conference where this talk would fit well? Would the team at your current workplace benefit from listening to your talk for an hour? If you write it, people will come.
And as an extra tip 
  • Transform your speaker notes into search engine fodder
    I need to do this more often. I've seen more organised folk post their notes on GitHub, or as some sort of blog post. As a developer, sometimes I stumble across a great set of slides, but yearn for a little more context or content. It will always be useful for somebody, won't hurt your SEO, and you'll thank yourself immediately, and again in 6 months time when you google your own post.
How am I tackling my own submissions?

Slow at first, it's been a busy summer, but now I'm back at work building things, I feel like getting back into regular time aside.

I've submitted four ideas, to help my chances of being accepted. Maybe one idea is really good, but every other Tom, Dick, and Henrietta has submitted some variation of that idea. Perhaps your secondary submission fits rather well instead, among all the rest?

And given the thoughts above, I could make progress on all these ideas, knowing that they'll be useful somewhere along the line. Our local user group always seem happy to have me yak on about APEX.

Anyhoo, these were the titles for my Kscope20 submissions.
  • Oracle Reports to AOP Case Study
  • Visualising APEX Performance Monitoring
  • Navigating APEX Version Upgrades
  • A Practical Guide to APEX Authorisation Schemes
I was involved in the public voting exercise, and I not only saw some similar submissions, but a whole bunch of submissions on ideas I've considered, had, or wished that I had. Even if only a third of those submissions ever get produced, there's going to be some amazing content out there.

My titles are a little utilitarian, and lack some awesome word play I saw when reviewing abstracts, but I'm pretty excited about the building the content.

What am I doing to prepare these? I haven't got to this yet, have I?
  • AOP - I strategically chose this topic as I'm learning this as part of business as usual at work. My aim is to present a cheat-sheet style session to help new AOP developers hit the ground running. All I need to do is show up to work each day to prepare, though I need bed down the session structure.
  • APEX Performance monitoring - I've been writing charts & reports on these log tables for years, and I have the confidence I can piece together this presentation at will. I've offered different formats, so it may even be hands-on.
  • Version upgrades - I've done a few of these over time, and I've been making notes. I need to start playing on an empty canvas to help cultivate exactly how it will play out.
    And by this I mean probably both a simple text file that slowly fleshes out a list of key items I want to cover, and some sort of scratching/drawing the represents the story of my talk.
  • Authorisation Schemes - this one's been a real slow burn, something I've been wanting to do for a long time, but I hope to turn this into more than just a presentation. This is what I've been hinting about in this sites left menu bar, some form of publication.
    After piecing together a fairly clear breakdown of content, I may have procrastinated a little.
Regardless of where my experience is coming from to formulate the presentation, I typically start with a simple text file, flesh about my ideas, reorder as necessary, then start my slides.

Once the slides are done, I tend to go through them a few times, with a text file ready to note down simple modifications I want to make, without disrupting my flow too much.

Rinse and repeat, until your practice in front of a mirror/camera/friend comes out smooth, and on time.

Don't worry if you get nervous. We all get nervous.

PS - some of us have been living under metaphorical rocks, so if you've missed a bunch of posts by Martin Widlake on presenting, I recommend you check them out.