惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
云风的 BLOG
云风的 BLOG
IT之家
IT之家
C
Check Point Blog
T
The Blog of Author Tim Ferriss
S
SegmentFault 最新的问题
人人都是产品经理
人人都是产品经理
H
Hackread – Cybersecurity News, Data Breaches, AI and More
美团技术团队
M
MIT News - Artificial intelligence
Jina AI
Jina AI
Blog — PlanetScale
Blog — PlanetScale
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Microsoft Security Blog
Microsoft Security Blog
G
Google Developers Blog
F
Fortinet All Blogs
V
Visual Studio Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Tailwind CSS Blog
Hugging Face - Blog
Hugging Face - Blog
MyScale Blog
MyScale Blog
爱范儿
爱范儿
The Cloudflare Blog
博客园 - 三生石上(FineUI控件)

IT Jungle

Finally: Some Pricing Information On The Power S1112 Entry Server - IT Jungle Rocket Automates Manual IBM i Tasks With AI - IT Jungle Guru: Where’s The Table? - IT Jungle LaserVault Goes iSCSI With Virtual Tape Library - IT Jungle IBM i PTF Guide, Volume 28, Number 31 - IT Jungle Will Power Chips Get A Converged Arm Instruction Set Like Z Mainframe CPUs? - IT Jungle Thinking About Moving IBM i To The Cloud? Don't Start With The Quote - IT Jungle Precisely To Add Ransomware Protection In MIMIX 11 - IT Jungle It’s D-Day For Cybersecurity, AI Firms Warn - IT Jungle IBM i PTF Guide, Volume 28, Number 30 - IT Jungle Oracle Dips A Toe Into IBM’s EBCDIC World - IT Jungle When Your Small IBM i Team Is Really A Team Of One - IT Jungle Guru: Putting Failure Handling In Its Place - IT Jungle Inside The Security Enhancements In ACS - IT Jungle IBM i PTF Guide, Volume 28, Number 28: A Crazy Number of Security Vulnerability Patches - IT Jungle IBM i PTF Guide, Volume 28, Number 29 - IT Jungle IBM i PTF Guide, Volume 28, Number 28: A Crazy Number of Security Vulnerability Patches - IT Jungle Inside The Encryption Key Management Changes In IBM i 7.6 - IT Jungle FalconStor Moved To The Blue Lagoon, And Is Poised For Growth Because Of It - IT Jungle Guru: Claude’s SQL Tip - IT Jungle Astera Makes Extracting Legacy Report Data an AI Specialty - IT Jungle IBM i PTF Guide, Volume 28, Number 27 - IT Jungle Welcoming The New IBM i Chief Architect And Other New Top Brass - IT Jungle A Deep Dive Into That Power S1112 Entry Power11 Server - IT Jungle Guru: Beyond Three-Part Naming – Running SQL Across Remote IBM i Systems - IT Jungle How IBM Bolstered IBM i Resilience In The Summer Tech Refreshes - IT Jungle IBM i PTF Guide, Volume 28, Number 26 - IT Jungle Power Systems Has A Great Quarter; System Z, Not So Much - IT Jungle Does AI Mark The End Of The ERP Era? - IT Jungle Guru: Deterministic Application Development With AI - IT Jungle
Guru: Load A Varying-Dimension Array With One SQL Fetch -...
Ted Holt · 2026-05-11 · via IT Jungle

May 11, 2026

One thing I really like about working in different shops is the vast amount of source code I’m exposed to. But that’s not the best thing. The best thing is the people I meet and get to know. The more clients, the more teachers. Combine source code and people, and my life, personally and professionally, is enriched.

Today I’m pleased to pass along an SQL technique I picked up from the IT shop of a manufacturer. The programmers use this technique to load subfiles from SQL cursors in RPG programs. It works in client-server applications as well. It combines a multi-row fetch with a varying-dimension array. The result is an easy, powerful way to retrieve and manipulate data. Let me show you what I mean.

A multi-row fetch retrieves more than one row from the database into memory in one operation. This type of fetch requires that the for n rows clause be added to the fetch statement, like this:

 Exec Sql
    Fetch C1 for 250 ROWS INTO :ResultSet;

ResultSet is an array of data structures.

In this shop, the programmers had found a way to load a varying-dimension array with one fetch. It goes like this:

  • Declare and open a cursor.
  • Use Get Diagnostics to retrieve the number of rows in the result set.
  • Use the %elem built-in function to set the dimension of the result set array.

And that’s all there is to it. Almost. There is one little wrinkle that can derail the entire process. The cursor must be insensitive.

This story contains code, which you can download here.

Sometimes we forget about the sensitivity options: sensitive, insensitive, and asensitive. This option specifies how current the data needs to be. If you need to see changes as users insert, update, and delete against the underlying tables, you need a sensitive cursor.

For most of the SQL queries I’ve embedded in programs, sensitivity didn’t matter. However, in this case it matters greatly. If the cursor is not insensitive, Get Diagnostics will not return the correct row count.

Once the data has been copied into the array, you have the full set of array options, such as lookup and sorting, at your fingertips.

I’ve prepared a little example program to illustrate how this works.

**free
ctl-opt actgrp(*new) main(PCTL0013R)
        option(*srcstmt: *nounref: *nodebugio)
        debug(*RetVal);

exec sql  set option datfmt=*iso;

dcl-ds  ResultSet  ExtName('PRODUC01') qualified alias dim(*var: 100);
end-ds;

dcl-s  ResultSet_Rows    uns(10) ;

// Sort sequences
dcl-enum SortBy   qualified;
   Order                    'ORDER';
   Customer                 'CUSTOMER';
   DueDate                  'DUEDATE';
end-enum SortBy;

dcl-proc PCTL0013R;
   dcl-pi *n;
      inPartNumber      char(3)     const;
   end-pi;

   MainRoutine (inPartNumber);

end-proc PCTL0013R;

dcl-proc MainRoutine;
   dcl-pi *n;
      inPartNumber      char(3)     const;
   end-pi;

   LoadResultSet (inPartNumber);
   SortResultSet (SortBy.Order);
   SortResultSet (SortBy.Customer);

end-proc MainRoutine;

dcl-proc LoadResultSet;
   dcl-pi *n;
      inPartNumber      char(3)     const;
   end-pi;

   Exec Sql Declare PCTL0013R_1 insensitive Cursor for

      select p.*
        from ProductionView as p
       where p.PartNumber = :inPartNumber
       order by p.DueDate, p.OrderNumber
         for read only;

   Exec Sql  Open PCTL0013R_1 ;

   Exec Sql  Get Diagnostics :ResultSet_Rows = DB2_NUMBER_ROWS ;

   %Elem(ResultSet) = ResultSet_Rows ;

   If ResultSet_Rows > *zero ;
      Exec Sql
         Fetch PCTL0013R_1 for :ResultSet_Rows ROWS INTO :ResultSet;
   EndIf ;

   Exec Sql  Close PCTL0013R_1 ;

end-proc LoadResultSet;

dcl-proc SortResultSet;
   dcl-pi *n;
      inSequence        char(12)   const;
   end-pi;

   select inSequence;
      when-is SortBy.Order;
         SortA ResultSet %Fields(CustomerNumber: DueDate);
      when-is SortBy.Customer;
         SortA ResultSet %Fields(OrderNumber);
      other;
         SortA ResultSet %Fields(DueDate);
   EndSl;

end-proc SortResultSet;

Imagine an end user whose job is to schedule production in a factory. You use this program, PCTL0013R, to retrieve the data for a part that the scheduler wishes to have built. The data is in four tables – sales order header, sales order detail, customer master, and item master – over which the database engineer has built a view, ProductionView, which has the system name PRODUC01.

This is the varying-dimension array of data structures for the SQL result set.

dcl-ds  ResultSet  ExtName('PRODUC01') qualified alias dim(*var: 100);
end-ds;

The alias keyword makes the array have the same fields as the column names in ProductionView. Unless I’m working on an old program that requires the system names in order to function correctly, I typically use the alias keyword to take advantage of longer field names.

Notice that I’ve defined ResultSet as a module-level (global) variable. I have written numerous times about my hatred for global variables, but in some cases it makes sense to declare a variable globally, especially when the entire program centers around that variable.

Here’s the cursor definition.

Exec Sql Declare PCTL0013R_1 insensitive Cursor for
   select p.*
     from ProductionView as p
    where p.PartNumber = :inPartNumber
    order by p.DueDate, p.OrderNumber
      for read only;

Exec Sql  Open PCTL0013R_1 ;

The cursor is defined as insensitive. If you wish, you can also add scroll, but I’ve never felt that I needed a scrollable cursor, as I only access the data once. I’ve added the for read only clause to the select. I don’t know if that’s necessary or not, but the programmers in that shop say that it helps. I think it may give the SQL engine more information to make a better decision. Better safe than sorry.

Now that the insensitive cursor is open, Get Diagnostics knows how many rows will be in the result set. Set the dimension of the array.

%Elem(ResultSet) = ResultSet_Rows ;

Now you can use the built-in %ELEM function as needed and won’t be needing %SUBARR quite as often.

By the way, whenever you need to see the current dimension of the array in the debugger, look at the _QRNU_VARDIM_ELEMS_RESULTSET variable. Replace RESULTSET with the name of your array.

Fetch the rows.

If ResultSet_Rows > *zero ;
   Exec Sql
      Fetch PCTL0013R_1 for :ResultSet_Rows ROWS INTO :ResultSet;
EndIf ;

You’re finished using the cursor. Close it. The data is in the array and you can do whatever you wish with it. Since the shop in question uses a green-screen interface, their programs load the array into a subfile, sometimes after sorting the array. This example program has no user interface. Such a program can easily pass data to a calling program that has no database I/O.

I mentioned lookup and sorting. You can use any array operation. I added a sort routine to my example program.

// Sort sequences
dcl-enum SortBy   qualified;
   Order                    'ORDER';
   Customer                 'CUSTOMER';
   DueDate                  'DUEDATE';
end-enum SortBy;

SortResultSet (SortBy.Order);
SortResultSet (SortBy.Customer);

dcl-proc SortResultSet;
   dcl-pi *n;
      inSequence        char(12)   const;
   end-pi;

   select inSequence;
      when-is SortBy.Customer;
         SortA ResultSet %Fields(CustomerNumber: DueDate);
      when-is SortBy.Order;
         SortA ResultSet %Fields(OrderNumber);
      other;
         SortA ResultSet %Fields(DueDate);
   EndSl;

end-proc SortResultSet;

I used an enumeration to define and group the sort sequence constants. Using named constants instead of quoted literals means the compiler will catch any spelling errors. I continue to look for effective ways to use enumerations.

The downloadable code file contains this program plus some SQL to build the example data, in case you’d like to play with this technique. The RPG program includes a DumpResultSet subprocedure, which I often include for development, testing, and debugging, but which the client programs don’t usually call. Also, be aware that I omitted error-handling code. In a production program, I would check the SQLSTATE variable as required.

I am grateful to Mike Odom for introducing me to this method of data retrieval.

As always, please share with me your experiences, especially improvements to what I’ve published or mistakes I’ve made. You can reach me through the IT Jungle Contact page.

Ted Holt is the original, the one and only, chief of the Four Hundred Gurus. We are glad he is back with us writing technical material that helps IBM i programmers. He is a self-employed, independent programmer living near Tupelo, Mississippi, who is old enough to retire but is not ready to do so. He still enjoys programming and is available to help others as needed. He welcomes your comments, questions, and suggestions.

RELATED STORIES

The Four Hundred Guru Retires

Guru: Dynamic Arrays Come To RPG

Guru: Dynamic Arrays Come To RPG – The Next Part Of The Story

Guru: Dynamic Arrays Come To RPG – Limitations, Circumventions, And More

Guru: Global Variables in Modules

Guru: Assertions, Take 2

Guru: Using Mixed Lists To Add “Data Structures” To CL Commands

Guru: TryIT – You’ll Like It

Guru: Aliases — Underused and Unappreciated

Guru: Beware of SQL Precompiler Variables

Guru: The SND-MSG Op Code And Message Subfiles

Guru: The CALL I’ve Been Waiting For

The Four Hundred Guru Retires

Guru: Global Variables in Modules

Guru: Abstract Data Types and RPG

Guru: Flexible Interfaces

Guru: Quick And Handy RPG Output, Take 2

Guru: What Is Constant Folding And Why Should I Care About It?

Guru: Alternate SQL Row-Selection Criteria Revisited Revisited

Guru: Another Red Flag – Joining On Expressions

Guru: Set Beats A Loop

Guru: The Deception of Fractional Labeled Durations

Guru: Elapsed Time For Human Beings

Guru: One-Shot Requests and Quoted Column Names

Guru: Use SQL To Replace Reports

Guru: Date Format Confusion

Guru: Compare Pieces Of Source Members

Guru: Stub Testing And SQL

Guru: QCMDEXC Makes A Good CPP

Guru: SELECT INTO And Arrays

Guru: I’m A Number, You’re A Number, Everybody’s A Number

Guru: SQL PL, WHILE And REPEAT Loops