Record Groups in Oracle Forms — What They Are and How to Use Them

If you have worked with Oracle Forms LOVs or populated list items dynamically, you have already used Record Groups — even if you did not know it. Record Groups are one of those foundational Oracle Forms objects that operate quietly behind the scenes, powering some of the most important data-driven features in your forms.

In this guide, we break down exactly what Record Groups are, the different types available, and how to use them effectively in real-world Oracle Forms applications.

What Is a Record Group?

A Record Group is an internal Oracle Forms object that stores tabular data — rows and columns — in memory. Think of it as a temporary, in-memory result set that your form can use for various purposes.

Record Groups are used to:

  • Power LOVs (every LOV has a Record Group as its data source)
  • Populate list items (drop-down lists) at runtime
  • Store intermediate query results for processing
  • Pass data between form blocks or between forms
  • Perform custom multi-record operations without database commits

Types of Record Groups

Oracle Forms supports three types of Record Groups, each serving a different purpose:

1. Query Record Group

The most common type. It is based on a SQL SELECT statement. When you populate a Query Record Group, Oracle Forms runs the SQL query against the database and loads the results into memory.

-- Defined at design time in the Record Group properties
SELECT department_id, department_name
FROM   departments
WHERE  active_flag = 'Y'
ORDER BY department_name

The query is fixed at design time but can use bind variables that reference form items, allowing the results to be filtered dynamically at runtime.

2. Static Record Group

A Static Record Group contains data that you define manually at design time — no database query involved. You specify the columns and rows directly in Forms Builder.

Use this for small, fixed lists of values that will never change, such as:

  • Status codes (Active, Inactive, Pending)
  • Priority levels (High, Medium, Low)
  • Yes/No lists

Static Record Groups load instantly because they do not require a database round trip.

3. Non-Query Record Group

A Non-Query Record Group is created entirely through PL/SQL code at runtime. You define the structure (columns and types) and add rows programmatically. This is the most flexible type and gives you complete control over the data.

Working with Record Groups in PL/SQL

Record Groups can be manipulated entirely through PL/SQL built-in functions. Here are the most important ones:

Finding a Record Group

DECLARE
  rg_id RECORDGROUP;
BEGIN
  rg_id := FIND_GROUP('DEPT_LOV_RG');
  IF ID_NULL(rg_id) THEN
    MESSAGE('Record Group not found.');
  END IF;
END;

Populating a Query Record Group

DECLARE
  rg_id RECORDGROUP;
  err   NUMBER;
BEGIN
  rg_id := FIND_GROUP('DEPT_LOV_RG');
  err   := POPULATE_GROUP(rg_id);
  
  IF err <> 0 THEN
    MESSAGE('Error populating record group: ' || TO_CHAR(err));
  END IF;
END;

Creating a Non-Query Record Group at Runtime

DECLARE
  rg_id   RECORDGROUP;
  col_id  GROUPCOLUMN;
  err     NUMBER;
BEGIN
  -- Create the group structure
  rg_id := CREATE_GROUP('TEMP_GROUP');
  col_id := ADD_GROUP_COLUMN(rg_id, 'CODE', CHAR_COLUMN, 10);
  col_id := ADD_GROUP_COLUMN(rg_id, 'LABEL', CHAR_COLUMN, 50);
  
  -- Add rows
  err := ADD_GROUP_ROW(rg_id, END_OF_GROUP);
  SET_GROUP_CHAR_CELL('TEMP_GROUP.CODE',  1, 'A');
  SET_GROUP_CHAR_CELL('TEMP_GROUP.LABEL', 1, 'Option A');
  
  err := ADD_GROUP_ROW(rg_id, END_OF_GROUP);
  SET_GROUP_CHAR_CELL('TEMP_GROUP.CODE',  2, 'B');
  SET_GROUP_CHAR_CELL('TEMP_GROUP.LABEL', 2, 'Option B');
END;

Reading Values from a Record Group

DECLARE
  rg_id    RECORDGROUP;
  row_cnt  NUMBER;
  code_val VARCHAR2(10);
BEGIN
  rg_id   := FIND_GROUP('TEMP_GROUP');
  row_cnt := GET_GROUP_ROW_COUNT(rg_id);
  
  FOR i IN 1..row_cnt LOOP
    code_val := GET_GROUP_CHAR_CELL('TEMP_GROUP.CODE', i);
    -- Process each row
    DBMS_OUTPUT.PUT_LINE('Row ' || i || ': ' || code_val);
  END LOOP;
END;

Deleting a Record Group

DELETE_GROUP('TEMP_GROUP');

Always delete dynamically created Record Groups when you are done with them to free memory. Groups created at design time persist for the life of the form session.

Populating a List Item from a Record Group

One powerful use of Record Groups is dynamically populating a drop-down list item. This is useful when the available options depend on runtime context.

DECLARE
  rg_id RECORDGROUP;
  err   NUMBER;
BEGIN
  -- Use a predefined Query Record Group
  rg_id := FIND_GROUP('STATUS_LIST_RG');
  err   := POPULATE_GROUP(rg_id);
  
  IF err = 0 THEN
    -- Populate the list item from the record group
    POPULATE_LIST('BLOCK.STATUS_ITEM', rg_id);
  END IF;
END;

The POPULATE_LIST built-in replaces all existing list item values with the contents of the Record Group. The first column becomes the display label and the second column becomes the return value.

Using Bind Variables in Record Group Queries

Query Record Groups support bind variables that reference form items. This allows the same Record Group to return different data based on the current form context:

-- Record Group query with bind variable
SELECT product_id, product_name, unit_price
FROM   products
WHERE  category_id = :ORDERS.CATEGORY_ID
AND    active = 'Y'
ORDER BY product_name

When you call POPULATE_GROUP, Oracle Forms substitutes the current value of :ORDERS.CATEGORY_ID automatically. The results are specific to the currently selected category in the ORDERS block.

Modifying the Query at Runtime

Sometimes you need to change the SQL query of a Record Group dynamically at runtime. Use POPULATE_GROUP_WITH_QUERY:

DECLARE
  rg_id  RECORDGROUP;
  err    NUMBER;
  v_sql  VARCHAR2(1000);
BEGIN
  rg_id := FIND_GROUP('CUSTOM_RG');
  
  -- Build a dynamic query
  v_sql := 'SELECT emp_id, full_name FROM employees WHERE dept_id = ' 
            || TO_CHAR(:DEPT_BLOCK.DEPT_ID);
  
  err := POPULATE_GROUP_WITH_QUERY(rg_id, v_sql);
  
  IF err <> 0 THEN
    MESSAGE('Query failed.');
  END IF;
END;

Security note: Be careful with dynamic SQL in Record Groups, especially if any part of the query is derived from user input. Always use bind variables where possible to avoid SQL injection risks.

Common Mistakes with Record Groups

  • Not populating before using: A Query Record Group defined at design time is empty until you call POPULATE_GROUP. If you use it in an LOV without populating it, the LOV will show no data.
  • Forgetting to delete dynamic groups: Creating Record Groups in frequently called triggers without deleting them leads to memory accumulation.
  • Column count mismatch in POPULATE_LIST: The Record Group must have at least 2 columns for POPULATE_LIST. The first is the label (displayed to user) and the second is the value (stored in the item).
  • Bind variable not resolving: If a bind variable in the Record Group query refers to a NULL item, the query may return no rows. Add a check before calling POPULATE_GROUP.

Summary

Record Groups are the backbone of dynamic data presentation in Oracle Forms. Whether you are powering an LOV, populating a dropdown list, or managing intermediate result sets in complex business logic, understanding Record Groups is essential.

The three types — Query, Static, and Non-Query — cover almost every scenario you will encounter, and the PL/SQL built-in functions give you complete programmatic control. Once you are comfortable with them, you will find Record Groups appearing naturally in your solutions to data management challenges.

PreviousNext