Tuesday, 18 February 2020

APEX Component Settings for Switch

While I point out a 'hidden' setting for Switch items in APEX, I want to comment on two related item types found in Oracle APEX => radio buttons and checkboxes.


1) Radio button

I really think the standard Template Option for radio groups should 'Display as Pill Button'

It really provides a simple and effective UX when a small set of options are on offer. Touch devices can select the option as if it's a button, and mouse users only need one click, compared to a select list requiring two. And the click doesn't need to be precise for those tiny radio group circles. I wish the application builder adopted the pill button approach more often.

And it's real easy to hook a dynamic action on change of the radio group, perhaps to refresh a classic report with data limited to the selected option. (Don't forget to add page items to submit!)

2) Switch

The Switch item was the answer to a number of methods of deploying an "iPhone-like" on/off switch.
I actually wrote an (internal) item plugin to do just this, but I much prefer using the native option.

A frequent question relates to how switches are rendered. If you don't see what you expect, have a look in Shared Components -> Component Settings.
Application -> Shared Components -> Component Settings

Personally I prefer the 'Pill Button' look, for reasons above. I find the APEX Application Builder seems more washed out with all the Switches in the properties bar, compared to the more softened On/Off pill buttons. If I could change this at the builder level, I would.

The offerings will vary depending on your APEX version, and if the application's Universal Theme has been refreshed

Application level settings for plugins can also be found in Component Settings, in addition to built-in APEX feature tweaks. These can something you choose to review after an APEX version upgrade.

3) Checkbox

It seems the humble HTML checkbox will always be extant, regardless of the pain it brings to tabuler forms. However, it suffers the same precision requirement as the native radio group selections - though better deployments allow selection using the label as well.

Go with the Switch (as a pill).

What do you think?


Monday, 10 February 2020

Validate data type within SQL

For all those pushing data around, especially dirty data, this one is for you.

Today I was preparing to process data I loaded from a spreadsheet.
A simple filter was required - to ignore the header row, had it been included.

I'm lucky enough to be working on 19c, and I remembered that a reasonably new function should help me out with all many of data loading issues. With a quick scan of my favourite reference manual, I found VALIDATE_CONVERSION.

For example, this gives me 'ORA-01722 invalid number' because of the header row I failed to exclude.
select c.*
from my_data_load c
order by to_number(seq);
But without the to_number, the order returns incorrectly.
SEQ
-----
1
10
12
140
2
Order
Sure, we could say
where seq != 'Order'

But this tool will have more than one use
select c.*
from my_data_load c
where validate_conversion(seq as number) = 1
order by to_number(seq);

SEQ
-----
1
2
10
12
140

Recreate this result using
select * from (
select 'Order' seq from dual
union all select '1' from dual
union all select '2' from dual
union all select '10' from dual
union all select '12' from dual
union all select '140' from dual
)
where validate_conversion(seq as number) = 1
order by to_number(seq)
And see typical return values (0 or 1) for conversion attempts using
select
   validate_conversion('1' as number) num1
  ,validate_conversion('2' as number) num2
  ,validate_conversion('1b' as number)  num_not
  ,validate_conversion('01-01-2001' as date) date1
  ,validate_conversion('30-02-2000' as date, 'dd-mm-yyyy') date2
from dual;  

      NUM1       NUM2    NUM_NOT      DATE1      DATE2
---------- ---------- ---------- ---------- ----------
         1          1          0          0          0
It's one of a few tools I'm using to make data loading life easier, and processing data in sets using SQL, not looping & context switching within PL/SQL.

The kicker, turns out this has been available since 12.2

More examples available from
LiveSQL
Tim Hall
Oren Nakdimon
19c Documentation

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.
SELECT ...
           f.substitution_string_18,
           f.substitution_value_18,
           f.substitution_string_19,
           f.substitution_value_19,
           f.substitution_string_20,
           f.substitution_value_20
      from wwv_flow_authorized auth,
           wwv_flows f
     where f.id = auth.application_id )
select workspace,
       workspace_display_name,
       application_id,
       application_name,
       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,
       workspace_display_name,
       application_id,
       application_name,
       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.
iterations:5000
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.

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 
WHERE SYSDATE BETWEEN eff_start AND COALESCE(eff_end, SYSDATE)
ORDER by 1
; end;
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 sm.id = m.id) 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.

#physics

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?

No.

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.