I have a stored procedure that counts distinct value for a set of columns to report new values to users if they arise. The inventory of columns is in a table so we can add new values to the check. The procedure loops through this inventory, generates a select statement, uses it to open a cursor, and then fetches results and inserts them into the results table one by one.
My background is Oracle and I'm fairly new with writing procedures in DB2. I'm wondering if there's an approach using arrays that would cut down on some of the steps. I'm working with DB2 11.5 luw, and Oracle Compatibility Turned on, so I have a working example using Oracle Syntax. I have a mostly working DB2 equivalent but haven't figured if there's a way to do the dynamic select into an array. Anybody know if this is possible? ...and any good recommended reading for writing procedures in DB2?
Sample Tables
CREATE TABLE cols (
tabschema varchar(128)
, tabname varchar(128)
, colname varchar(128)
)
ORGANIZE BY ROW;
CREATE TABLE cols_values(
tabschema varchar(128)
, tabname varchar(128)
, colname varchar(128)
, col_value varchar(200)
, value_count integer
)
ORGANIZE BY ROW;
INSERT INTO cols
VALUES
('SYSCAT', 'TABLES', 'OWNERTYPE')
, ('SYSCAT', 'TABLES', 'STATUS')
, ('SYSCAT', 'TABLES', 'TABSCHEMA');
Oracle PL/SQL Version
CREATE OR REPLACE PACKAGE ora_ver
IS
TYPE result_row_type IS RECORD
(
col_value varchar(200)
, value_count integer
);
TYPE result_array_type IS TABLE OF result_row_type INDEX BY INTEGER;
PROCEDURE get_cols_values;
END;
CREATE OR REPLACE PACKAGE BODY ora_ver
IS
PROCEDURE get_cols_values
IS
l_sel_stmnt VARCHAR(500);
l_results result_array_type;
BEGIN
FOR col IN (SELECT * FROM COLS ORDER BY tabschema, tabname, colname)
LOOP
l_sel_stmnt := 'SELECT ' || col.colname || ', COUNT(*) FROM ' || col.tabschema || '.' || col.tabname || ' GROUP BY ' || col.colname;
EXECUTE IMMEDIATE l_sel_stmnt BULK COLLECT INTO l_results;
FORALL i IN 1..l_results.count
INSERT INTO cols_values
(
tabschema
, tabname
, colname
, col_value
, value_count
)
VALUES
(
col.tabschema
, col.tabname
, col.colname
, l_results(i).col_value
, l_results(i).value_count
);
END LOOP;
END get_cols_values;
END ora_ver;
DB2 Version
CREATE OR REPLACE MODULE db2_ver;
ALTER MODULE db2_ver ADD TYPE result_row_type AS ROW
(
col_value varchar(200)
, value_count integer
);
ALTER MODULE db2_ver ADD TYPE result_array_type AS result_row_type ARRAY[];
ALTER MODULE db2_ver DROP PROCEDURE get_cols_values ;
ALTER MODULE db2_ver PUBLISH PROCEDURE get_cols_values ()
BEGIN
DECLARE l_sel_stmnt VARCHAR(500);
DECLARE l_results result_array_type;
FOR col AS (SELECT * FROM COLS ORDER BY tabschema, tabname, colname)
DO
SET l_sel_stmnt = 'SELECT ' || col.colname || ', COUNT(*) FROM ' || col.tabschema || '.' || col.tabname || ' GROUP BY ' || col.colname;
-- ************************************************************
-- Is there a DB2 equivalent of this? ARRAY_AGG?
-- EXECUTE IMMEDIATE l_sel_stmnt BULK COLLECT INTO l_results;
-- ************************************************************
INSERT INTO cols_values
(
tabschema
, tabname
, colname
, col_value
, value_count
)
SELECT
col.tabschema
, col.tabname
, col.colname
, t.col_value
, t.value_count
FROM UNNEST(l_results) AS T(col_value, value_count);
END FOR;
END;