How to Manage All Lookup Values from One Page in Oracle APEX

Keep every short list in two lookup tables, maintain them on one master-detail page, and use shared lists of values that hide retired values but keep old records.

Most applications have many short lists: priorities, categories, statuses, request types. When each list is hard-coded in a static list of values, every new entry needs a developer and a new release of the app.

A lookup table keeps all these lists in two tables, one for the list types and one for their values. In Oracle APEX, one master-detail page lets an administrator maintain every list. Shared lists of values then read the values, hide the ones that are no longer used, and still show them on old records.

This guide uses help requests with a priority and a category as the example. The tables and the page work for any list in any application.

Step 1: Create the Tables

LOOKUP_TYPES holds one row for each list, such as PRIORITY. LOOKUP_VALUES holds the codes and display texts of each list, with a sort sequence and an active flag. HELP_REQUESTS is the table that uses two of the lists.

The unique key on LOOKUP_TYPE and LOOKUP_CODE allows each code only once in a list. Check constraints keep the codes in upper case and the flag at Y or N:

Example:

create table lookup_types (
  lookup_type  varchar2(30 char)   not null,
  type_name    varchar2(80 char)   not null,
  description  varchar2(400 char),
  constraint lookup_types_pk primary key (lookup_type),
  constraint lookup_types_upper_ck check (lookup_type = upper(lookup_type))
);

create table lookup_values (
  lookup_value_id  number generated by default on null as identity,
  lookup_type      varchar2(30 char)  not null,
  lookup_code      varchar2(30 char)  not null,
  meaning          varchar2(80 char)  not null,
  display_seq      number default on null 10 not null,
  is_active        varchar2(1 char) default on null 'Y' not null,
  constraint lookup_values_pk primary key (lookup_value_id),
  constraint lookup_values_type_fk foreign key (lookup_type)
    references lookup_types (lookup_type),
  constraint lookup_values_code_uk unique (lookup_type, lookup_code),
  constraint lookup_values_upper_ck check (lookup_code = upper(lookup_code)),
  constraint lookup_values_active_ck check (is_active in ('Y', 'N'))
);

create table help_requests (
  request_id     number generated by default on null as identity,
  subject        varchar2(200 char)  not null,
  priority_type  varchar2(30 char) generated always as ('PRIORITY') virtual,
  priority_code  varchar2(30 char)  not null,
  category_type  varchar2(30 char) generated always as ('REQUEST_CATEGORY') virtual,
  category_code  varchar2(30 char),
  created_on     date default on null sysdate not null,
  constraint help_requests_pk primary key (request_id),
  constraint help_requests_priority_fk foreign key (priority_type, priority_code)
    references lookup_values (lookup_type, lookup_code),
  constraint help_requests_category_fk foreign key (category_type, category_code)
    references lookup_values (lookup_type, lookup_code)
);

create index help_requests_priority_ix on help_requests (priority_code);
create index help_requests_category_ix on help_requests (category_code);

insert into lookup_types (lookup_type, type_name, description)
values ('PRIORITY', 'Priority', 'How urgent a request is');
insert into lookup_types (lookup_type, type_name, description)
values ('REQUEST_CATEGORY', 'Request Category', 'What a request is about');

insert into lookup_values (lookup_type, lookup_code, meaning, display_seq)
values ('PRIORITY', 'LOW', 'Low', 10);
insert into lookup_values (lookup_type, lookup_code, meaning, display_seq)
values ('PRIORITY', 'MEDIUM', 'Medium', 20);
insert into lookup_values (lookup_type, lookup_code, meaning, display_seq)
values ('PRIORITY', 'HIGH', 'High', 30);
insert into lookup_values (lookup_type, lookup_code, meaning, display_seq)
values ('PRIORITY', 'URGENT', 'Urgent', 40);
insert into lookup_values (lookup_type, lookup_code, meaning, display_seq)
values ('REQUEST_CATEGORY', 'HARDWARE', 'Hardware', 10);
insert into lookup_values (lookup_type, lookup_code, meaning, display_seq)
values ('REQUEST_CATEGORY', 'SOFTWARE', 'Software', 20);
insert into lookup_values (lookup_type, lookup_code, meaning, display_seq)
values ('REQUEST_CATEGORY', 'ACCESS', 'Access', 30);

insert into help_requests (subject, priority_code, category_code)
values ('Laptop does not start', 'URGENT', 'HARDWARE');
insert into help_requests (subject, priority_code, category_code)
values ('Install the reporting tool', 'MEDIUM', 'SOFTWARE');
insert into help_requests (subject, priority_code, category_code)
values ('New user needs a VPN account', 'HIGH', 'ACCESS');
commit;

select lookup_type, lookup_code, meaning, display_seq, is_active
from   lookup_values
order  by lookup_type, display_seq;

select request_id, subject, priority_type, priority_code, category_code
from   help_requests
order  by request_id;

Output:

Table LOOKUP_TYPES created.

Table LOOKUP_VALUES created.

Table HELP_REQUESTS created.

Index HELP_REQUESTS_PRIORITY_IX created.

Index HELP_REQUESTS_CATEGORY_IX created.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

Commit complete.

LOOKUP_TYPE         LOOKUP_CODE    MEANING        DISPLAY_SEQ IS_ACTIVE
___________________ ______________ ___________ ______________ ____________
PRIORITY            LOW            Low                     10 Y
PRIORITY            MEDIUM         Medium                  20 Y
PRIORITY            HIGH           High                    30 Y
PRIORITY            URGENT         Urgent                  40 Y
REQUEST_CATEGORY    HARDWARE       Hardware                10 Y
REQUEST_CATEGORY    SOFTWARE       Software                20 Y
REQUEST_CATEGORY    ACCESS         Access                  30 Y

7 rows selected.

   REQUEST_ID SUBJECT                         PRIORITY_TYPE    PRIORITY_CODE    CATEGORY_CODE
_____________ _______________________________ ________________ ________________ ________________
            1 Laptop does not start           PRIORITY         URGENT           HARDWARE
            2 Install the reporting tool      PRIORITY         MEDIUM           SOFTWARE
            3 New user needs a VPN account    PRIORITY         HIGH             ACCESS
  • DISPLAY_SEQ and IS_ACTIVE use DEFAULT ON NULL, so a new value gets sequence 10 and the flag Y without any code.
  • PRIORITY_TYPE and CATEGORY_TYPE in HELP_REQUESTS are virtual columns with a fixed value. They let a normal foreign key check the code against the right list.
  • The unique key on LOOKUP_VALUES starts with LOOKUP_TYPE, so its index also serves the foreign key to LOOKUP_TYPES.

Test the foreign keys. The first insert uses a code from another list, and the second a code that does not exist:

Example:

insert into help_requests (subject, priority_code, category_code)
values ('Printer is offline', 'HARDWARE', 'HARDWARE');

insert into help_requests (subject, priority_code, category_code)
values ('Printer is offline', 'CRITICAL', 'HARDWARE');

Output:

Error starting at line : 1 File @ b52/01-fk-test.sql
In command -
insert into help_requests (subject, priority_code, category_code)
values ('Printer is offline', 'HARDWARE', 'HARDWARE')
Error report -
ORA-02291: integrity constraint (APXSOL.HELP_REQUESTS_PRIORITY_FK) violated -
parent key not found
Help: https://docs.oracle.com/error-help/db/ora-02291/

Error starting at line : 4 File @ b52/01-fk-test.sql
In command -
insert into help_requests (subject, priority_code, category_code)
values ('Printer is offline', 'CRITICAL', 'HARDWARE')
Error report -
ORA-02291: integrity constraint (APXSOL.HELP_REQUESTS_PRIORITY_FK) violated -
parent key not found
Help: https://docs.oracle.com/error-help/db/ora-02291/

HARDWARE is a valid code, but it belongs to REQUEST_CATEGORY, not to PRIORITY. Both inserts were rejected by HELP_REQUESTS_PRIORITY_FK, so the database itself keeps wrong codes out. No trigger is needed.

Step 2: Create the Master-Detail Page

In App Builder, open the application and click Create Page. On the Component tab, click Master Detail:

Create a Page dialog with the Master Detail page type marked with a red box
The Master Detail page type.

Click the Stacked style. It puts two editable interactive grids on one page, the types above and the values below. Click Next:

Master Detail Style screen with the Stacked style marked with a red box
The Stacked style.

Enter Page Number 490 and Name Lookup Values. In the Master Data Source section, enter LOOKUP_TYPES as the Table / View Name:

Page Definition screen with page 490 Lookup Values and the table LOOKUP_TYPES marked with red boxes
The page and the master table.

Keep the Navigation settings, so the wizard adds a breadcrumb and a navigation menu entry. Click Next:

Navigation section of the wizard with Use Breadcrumb and Use Navigation turned on
The default navigation settings.

On the Primary Key screen, select LOOKUP_TYPE (Varchar2) as Primary Key Column 1 and click Next:

Primary Key screen with LOOKUP_TYPE selected and marked with a red box
The primary key of the master table.

On the Detail Data Source screen, enter Values as the Detail Region Title and LOOKUP_VALUES as the Table / View Name. Click Next:

Detail Data Source screen with the title Values and the table LOOKUP_VALUES marked with red boxes
The detail table.

Select LOOKUP_VALUE_ID (Number) as Primary Key Column 1. In Master Detail Foreign Key, select LOOKUP_TYPE -> LOOKUP_TYPE and click Create Page:

Detail Primary Key screen with LOOKUP_VALUE_ID and the foreign key LOOKUP_TYPE marked with red boxes
The detail key and the link to the master.

Step 3: Make the Lookup Type Editable

The wizard creates every primary key column as a Hidden column. That is right for LOOKUP_VALUE_ID, which the identity fills. But LOOKUP_TYPE is a code that the administrator must type, so a hidden column makes it impossible to add a new list.

Open page 490 in Page Designer. Select the first grid region and change its Name to Lookup Types, so the two grids are easy to tell apart:

First interactive grid region with Name Lookup Types marked with a red box
The master grid is now named Lookup Types.

Under Lookup Types, expand Columns and select LOOKUP_TYPE. Set Type to Text Field and Heading to Lookup Type:

LOOKUP_TYPE column with Type Text Field and Heading Lookup Type marked with red boxes
The type code becomes an editable column.

Under Settings, set Text Case to Upper. Under Validation, turn on Value Required:

LOOKUP_TYPE column with Text Case Upper and Value Required marked with red boxes
Codes are typed in upper case and cannot be empty.

Step 4: Set Up the Values Grid

Under Values, select the LOOKUP_CODE column. Set Heading to Code and, under Settings, Text Case to Upper. A user can type "critical" and the grid saves CRITICAL, which matches the check constraint:

LOOKUP_CODE column with Heading Code and Text Case Upper marked with red boxes
The code column.

Select DISPLAY_SEQ and set its Heading to Sequence. Select MEANING and keep it as it is.

Select IS_ACTIVE. Set Type to Switch and Heading to Active. The switch uses the application defaults Y and N, which match the check constraint:

IS_ACTIVE column with Type Switch and Heading Active marked with red boxes
The active flag as a switch.

Under Default, set Type to Static and Static Value to Y, so a new row in the grid starts as active:

IS_ACTIVE column with Default Type Static and Static Value Y marked with a red box
New values are active.

Step 5: Reject a Duplicate Code with a Clear Message

The unique key already stops a duplicate code. Test it in SQL:

Example:

insert into lookup_values (lookup_type, lookup_code, meaning)
values ('PRIORITY', 'HIGH', 'High again');

Output:

Error starting at line : 1 File @ b52/02-uk-test.sql
In command -
insert into lookup_values (lookup_type, lookup_code, meaning)
values ('PRIORITY', 'HIGH', 'High again')
Error report -
ORA-00001: unique constraint (APXSOL.LOOKUP_VALUES_CODE_UK) violated on table
APXSOL.LOOKUP_VALUES columns (LOOKUP_TYPE, LOOKUP_CODE)
ORA-03301: (ORA-00001 details) row with column values (LOOKUP_TYPE:'PRIORITY',
LOOKUP_CODE:'HIGH') already exists
Help: https://docs.oracle.com/error-help/db/ora-00001/

That message is fine for a developer but not for an administrator. A validation gives a readable message on the right cell.

On the Processing tab, right-click Validating and click Create Validation. Enter Name Code only once per type, set Editable Region to Values and Type to No Rows returned, and enter this query:

Example:

select 1
from   lookup_values
where  lookup_type = :LOOKUP_TYPE
and    lookup_code = upper(:LOOKUP_CODE)
and    to_char(lookup_value_id) <> nvl(:LOOKUP_VALUE_ID, 'NEW')
Validation Code only once per type with Editable Region Values, Type No Rows returned and the query marked with red boxes
The validation runs for each changed row of the Values grid.

Set Error Message to Code &LOOKUP_CODE. already exists for this lookup type. and Associated Column to LOOKUP_CODE. Under Server-side Condition, set Type to Expression, Language PL/SQL, and enter this expression:

Example:

:APEX$ROW_STATUS in ('C', 'U')

The validation then runs only for created and updated rows:

Error Message, Associated Column and the Server-side Condition of the validation marked with red boxes
The validation runs only for created and updated rows.

A new row has no LOOKUP_VALUE_ID yet, so the query compares it with the text NEW. The UPPER call matches the Text Case setting of the column. Click Save.

Step 6: Sort the Values by Sequence

Run page 490. The values appear in no fixed order, because an interactive grid uses its own saved report for sorting. In a first test, an ORDER BY in the grid's SQL query was not kept: after an update, URGENT (40) was shown after CRITICAL (50).

Sign in to App Builder in the same browser, so the page runs as a developer. In the Values grid, click Actions, then Data, then Sort:

Actions menu of the Values grid with Data and Sort marked with a red box
The Sort action of the Values grid.

Select Column Sequence and Direction Ascending, and click Save:

Sort dialog with Column Sequence and Direction Ascending marked with a red box
Sort by the sequence column.

Click Actions, then Report, then Save. APEX shows "Default report saved for all users":

Actions menu with Report and Save marked with a red box
Save the sort in the primary report.

After the save, ORDER_EDITOR, who is not a developer, saw the values in sequence order:

Lookup Values page for ORDER_EDITOR with the values sorted by Sequence
Every user sees the values in sequence order.

Step 7: Create a Shared List of Values for a Lookup Type

Each list gets its own shared list of values. Open Shared Components, click Lists of Values, and click Create:

Lists of Values page with the Create button marked with a red box
Create a new list of values.

Keep From Scratch and click Next:

Create List of Values dialog with From Scratch marked with a red box
Start from scratch.

Enter Name PRIORITY LOOKUP, keep Type Dynamic, and click Next:

Name and Type screen with PRIORITY LOOKUP and Dynamic marked with a red box
A dynamic list of values.

Select Source Type SQL Query and enter this query. Click Next:

Example:

select meaning     as display_value,
       lookup_code as return_value,
       display_seq
from   lookup_values
where  lookup_type = 'PRIORITY'
and    (is_active = 'Y' or lookup_code = :P492_PRIORITY_CODE)
List of Values Source screen with Source Type SQL Query and the query marked with a red box
The values of the PRIORITY list.

The condition on IS_ACTIVE hides values that are no longer used. The OR part keeps the value that the record already has. Without it, an old request with an inactive priority would show the code instead of its text, or nothing at all.

Select RETURN_VALUE as the Return Column and DISPLAY_VALUE as the Display Column. Click Create:

Column Mappings screen with RETURN_VALUE and DISPLAY_VALUE marked with a red box
The return and display columns.

Click PRIORITY LOOKUP in the list to edit it. Under Column Mapping, set Default Sort to DISPLAY_SEQ and click Apply Changes:

List of Values PRIORITY LOOKUP with Default Sort DISPLAY_SEQ marked with a red box
Sort the list by the sequence of the values.

Create a second list of values, REQUEST CATEGORY LOOKUP, in the same way, with this query and the same Default Sort:

Example:

select meaning     as display_value,
       lookup_code as return_value,
       display_seq
from   lookup_values
where  lookup_type = 'REQUEST_CATEGORY'
and    (is_active = 'Y' or lookup_code = :P492_CATEGORY_CODE)

Step 8: Create the Report and Form Pages

Click Create Page. On the Component tab, click Interactive Report:

Create a Page dialog with the Interactive Report page type marked with a red box
The Interactive Report page type.

Enter Page Number 491 and Name Help Requests. Turn on Include Form Page, enter Form Page Number 492 and Form Page Name Help Request, and select Form Page Mode Modal Dialog:

Create Interactive Report wizard with page 491 Help Requests and modal form page 492 Help Request marked with red boxes
The report page and a modal form page.

Enter HELP_REQUESTS as the Table / View Name:

Data Source section with the table HELP_REQUESTS marked with a red box
The table for the report and the form.

Keep the Navigation settings and click Next:

Navigation section of the wizard with the default settings
The default navigation settings.

Select REQUEST_ID (Number) as Primary Key Column 1 and click Create Page:

Primary Key screen with REQUEST_ID selected and marked with a red box
REQUEST_ID is the primary key.

Because of the composite foreign keys, the wizard creates select lists for PRIORITY_TYPE, PRIORITY_CODE, CATEGORY_TYPE and CATEGORY_CODE. It also creates two shared lists of values on LOOKUP_VALUES, LOOKUP_VALUES.LOOKUP_TYPE and LOOKUP_VALUES.LOOKUP_TYPE_1. They do not filter by list or by active flag, so they are not useful here.

Step 9: Use the Shared Lists of Values in the Form

Open page 492 in Page Designer. Delete the items P492_PRIORITY_TYPE and P492_CATEGORY_TYPE, because virtual columns cannot be inserted or updated. Delete P492_CREATED_ON too, because the column default fills it.

Select P492_PRIORITY_CODE. Keep Type Select List and change the Label to Priority:

P492_PRIORITY_CODE with Type Select List and Label Priority marked with red boxes
The priority select list.

Under List of Values, set Type to Shared Component and List of Values to PRIORITY LOOKUP. Turn off Display Extra Values, turn on Display Null Value, and enter - Select - as the Null Display Value:

List of Values group of P492_PRIORITY_CODE with PRIORITY LOOKUP and the null value settings marked with a red box
The item uses the shared list of values.

Select P492_CATEGORY_CODE, change its Label to Category, and set the same List of Values properties with REQUEST CATEGORY LOOKUP:

List of Values group of P492_CATEGORY_CODE with REQUEST CATEGORY LOOKUP marked with a red box
The category select list.

Click Save.

Step 10: Show the Meanings in the Report

The report must show Urgent for an old request even though Urgent is no longer active. The shared lists of values hide inactive values, so the report joins LOOKUP_VALUES directly.

Open page 491 and select the Help Requests region. Under Source, set Type to SQL Query and enter this query:

Example:

select r.request_id,
       r.subject,
       p.meaning as priority,
       c.meaning as category,
       r.created_on
from   help_requests r
join   lookup_values p
  on   p.lookup_type = r.priority_type
 and   p.lookup_code = r.priority_code
left   join lookup_values c
  on   c.lookup_type = r.category_type
 and   c.lookup_code = r.category_code
Help Requests region with Source Type SQL Query and the join query marked with a red box
The report joins each code to its meaning.

The join uses the virtual columns, so each code is looked up in its own list. Set the Heading of PRIORITY to Priority and of CATEGORY to Category, and click Save.

Now nothing uses the two lists of values that the wizard created. In Shared Components, delete LOOKUP_VALUES.LOOKUP_TYPE and LOOKUP_VALUES.LOOKUP_TYPE_1.

Step 11: Add a New Value

Run page 490 as an administrator and select PRIORITY in the Lookup Types grid. In the Values grid, click Add Row, type critical as the Code, Critical as the Meaning and 50 as the Sequence:

Values grid with a new row CRITICAL, Critical, 50, On marked with a red box
The code was typed in lower case and shown in upper case.

The Active switch is On by default. Click Save. The grid saves the row:

Lookup Values page after Save with CRITICAL in the Values grid
The new value is saved.

Check the table:

Example:

select lookup_type, lookup_code, meaning, display_seq, is_active
from   lookup_values
order  by lookup_type, display_seq;

Output:

LOOKUP_TYPE         LOOKUP_CODE    MEANING        DISPLAY_SEQ IS_ACTIVE
___________________ ______________ ___________ ______________ ____________
PRIORITY            LOW            Low                     10 Y
PRIORITY            MEDIUM         Medium                  20 Y
PRIORITY            HIGH           High                    30 Y
PRIORITY            URGENT         Urgent                  40 Y
PRIORITY            CRITICAL       Critical                50 Y
REQUEST_CATEGORY    HARDWARE       Hardware                10 Y
REQUEST_CATEGORY    SOFTWARE       Software                20 Y
REQUEST_CATEGORY    ACCESS         Access                  30 Y

8 rows selected.

Step 12: Deactivate a Value

A value that is already used cannot be deleted, so it is deactivated. In the Values grid, set the Active switch of URGENT to Off and click Save:

Values grid with the URGENT row and Active Off marked with a red box
URGENT is turned off.

Output:

LOOKUP_TYPE         LOOKUP_CODE    MEANING        DISPLAY_SEQ IS_ACTIVE
___________________ ______________ ___________ ______________ ____________
PRIORITY            LOW            Low                     10 Y
PRIORITY            MEDIUM         Medium                  20 Y
PRIORITY            HIGH           High                    30 Y
PRIORITY            URGENT         Urgent                  40 N
PRIORITY            CRITICAL       Critical                50 Y
REQUEST_CATEGORY    HARDWARE       Hardware                10 Y
REQUEST_CATEGORY    SOFTWARE       Software                20 Y
REQUEST_CATEGORY    ACCESS         Access                  30 Y

8 rows selected.

Step 13: Test the Select List

Sign in as ORDER_EDITOR and open Help Requests. The report shows the meaning of each code, and Urgent for the old request even though Urgent is no longer active:

Help Requests report with the priorities Urgent, Medium and High
The report shows the meaning of every code.

Click Create. The Priority list shows Low, Medium, High and Critical. Critical is there without any change to the app, and Urgent is gone:

Help Request dialog with the Priority list open showing Low, Medium, High and Critical marked with a red box
A new request cannot select the inactive value.

Enter the subject Printer is offline, select Critical and Hardware, and click Create. APEX shows "Row created." and the report lists the new request with priority Critical.

Open the request Laptop does not start. Its priority is Urgent, and the select list shows it, because the list of values keeps the current value of the record:

Help Request dialog for Laptop does not start with Priority Urgent marked with a red box
An old record keeps its inactive value.

Step 14: Block an Inactive Value on the Server

In a first test, a browser script added Urgent to the Priority list of a new request and clicked Create. APEX saved the request with the inactive value:

Output:

   REQUEST_ID SUBJECT                         PRIORITY_CODE    PRIORITY    IS_ACTIVE
_____________ _______________________________ ________________ ___________ ____________
            1 Laptop does not start           URGENT           Urgent      N
            2 Install the reporting tool      MEDIUM           Medium      Y
            3 New user needs a VPN account    HIGH             High        Y
           21 Printer is offline              CRITICAL         Critical    Y
           22 Forced inactive priority        URGENT           Urgent      N

APEX checks a select list value against its list of values on submit. But the list of values includes the item's own value, and that value is the forced URGENT, so the check passed. A validation closes this gap.

On page 492, open the Processing tab, right-click Validating and click Create Validation. Enter Name Priority must be active, set Type to No Rows returned, and enter this query:

Example:

select 1
from   lookup_values v
where  v.lookup_type = 'PRIORITY'
and    v.lookup_code = :P492_PRIORITY_CODE
and    v.is_active   = 'N'
and    not exists (
         select 1
         from   help_requests r
         where  r.request_id    = :P492_REQUEST_ID
         and    r.priority_code = v.lookup_code
       )
Validation Priority must be active with Type No Rows returned and the query marked with red boxes
The validation looks for an inactive priority that the record did not have before.

Set Error Message to This priority is no longer in use. Select another priority. Set Associated Item to P492_PRIORITY_CODE. Under Server-side Condition, set Type to Request is contained in Value and Value to CREATE,SAVE:

Error Message, Associated Item and the Server-side Condition of the validation marked with red boxes
The validation runs on Create and Apply Changes.

The NOT EXISTS part allows the inactive value only when the stored record already has it. Click Save. The forced request from the first test was deleted, and the same test was run again:

Help Request dialog with the error This priority is no longer in use
A forced inactive value is rejected.

Two more tests followed. ORDER_EDITOR changed the subject of the Urgent request and kept Urgent: the save worked. Then a script set Urgent on the Medium request: the validation rejected it. The table after all tests:

Output:

   REQUEST_ID SUBJECT                               PRIORITY_CODE    PRIORITY    IS_ACTIVE
_____________ _____________________________________ ________________ ___________ ____________
            1 Laptop does not start after update    URGENT           Urgent      N
            2 Install the reporting tool            MEDIUM           Medium      Y
            3 New user needs a VPN account          HIGH             High        Y
           21 Printer is offline                    CRITICAL         Critical    Y

Step 15: Test Duplicate Codes and Used Values

In the Values grid of PRIORITY, click Add Row and enter high, High again and 60. Click Save. The validation from Step 5 stops the save, shows its message and marks the Code cell:

Lookup Values page with the error Code HIGH already exists for this lookup type
A duplicate code is rejected.

Now delete the MEDIUM row, which the request Install the reporting tool uses, and click Save. The foreign key HELP_REQUESTS_PRIORITY_FK stops the delete:

Lookup Values page with the error ORA-02292 child record found
A value that is in use cannot be deleted.

Output:

LOOKUP_TYPE         LOOKUP_CODE    MEANING        DISPLAY_SEQ IS_ACTIVE
___________________ ______________ ___________ ______________ ____________
PRIORITY            LOW            Low                     10 Y
PRIORITY            MEDIUM         Medium                  20 Y
PRIORITY            HIGH           High                    30 Y
PRIORITY            URGENT         Urgent                  40 N
PRIORITY            CRITICAL       Critical                50 Y
REQUEST_CATEGORY    HARDWARE       Hardware                10 Y
REQUEST_CATEGORY    SOFTWARE       Software                20 Y
REQUEST_CATEGORY    ACCESS         Access                  30 Y

8 rows selected.

MEDIUM is still in the table. Deactivate a used value instead of deleting it.

Things to Know

  • A virtual column with a fixed list name, such as PRIORITY_TYPE, lets a normal composite foreign key check that a code belongs to the right list.
  • The Master Detail wizard creates every primary key column as Hidden. Change a natural key such as LOOKUP_TYPE to a Text Field, or no new list can be added.
  • An interactive grid ignores the ORDER BY of its source query. Set the sort with Actions, Data, Sort and save the primary report as a developer.
  • Including the item's own value in the list of values keeps old records readable, but it also lets a forced inactive value pass the built-in check. Add a validation like the one in Step 14 for every item that uses this pattern.
  • The form wizard creates unusable shared lists of values and select lists for composite foreign keys with virtual columns. Delete them and use one list of values per lookup type.
  • The ORA-02292 message for a used value comes straight from the database. Deactivating is the safe way to retire a value.

Related Guides

Conclusion

To manage all lookup values from one page in Oracle APEX, store the lists in a type table and a value table, and build a Stacked master-detail page on them. Each list gets a shared list of values that hides inactive values but keeps the current value of a record.

Constraints and virtual-column foreign keys protect the data in the database. A validation on the form blocks forced inactive values, and the saved grid report keeps the values in sequence order for every user.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE, author of four books on Oracle APEX, SQL and PL/SQL, and Oracle Forms, and a software developer building Oracle database applications since 2001.

guest

0 Comments
Oldest
Newest Most Voted