• The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
Menu
  • The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
  • Guru: Generate SQL for Dependents

    May 24, 2021 Paul Tuohy

    Way back in October 2014, in the article Find A View Of A View Of A View. . ., I detailed a stored procedure that, given the name of a table or a view, provides the full list of dependent views and all of their dependents and all of their dependents, etc. This is a stored procedure that has served me well in the world of DDL, where it is common to have views of views of views.

    In this article I will describe a follow-on stored procedure that will generate the DDL for a given table/view and all of its dependents. This means that when you are about to make a change to a table/view (add, change or delete one or more columns), you can immediately make any additional changes to dependents of the table/view.

    For example, let’s start by issuing this call to our stored procedure:

    call GENERATE_SQL_FOR_DEPENDENTS('B_L1USE', ‘TESTSTUFF');
    

    You are then provided with the following DDL returned as a result set from the call:

    --  Generate SQL                                                             
    --  Version:                   	V7R4M0 190621 
    --  Generated on:              	04/20/21 08:52:30 
    --  Relational Database:       	IDEVELOP 
    --  Standards Option:          	Db2 for i 
                                                                                
    CREATE OR REPLACE VIEW B_L1USE (       
    	DIVISION ,                                                       
    	DEPARTMENT ,                                                   
    	REP ,                                                                 
    	SALES )                                                             
    	AS                                                                       
    	SELECT DIVISION, DEPARTMENT, REP, SALES  
    		 FROM           BASESALE  
    		 WHERE REGION = 'USE'   
    	RCDFMT B_L1USE    ; 
      
    LABEL ON TABLE B_L1USE 
    	IS 'Sales for Region USE' ; 
      
    LABEL ON COLUMN B_L1USE 
    ( DIVISION IS 'Division             ' , 
    	DEPARTMENT IS 'Department           ' , 
    	REP IS 'Rep                  ' , 
    	SALES IS 'Sales                ' ) ; 
      
    GRANT DELETE , SELECT , UPDATE   
    ON B_L1USE TO PUBLIC ; 
      
    GRANT ALTER , DELETE , REFERENCES , SELECT , UPDATE   
    ON B_L1USE TO TUOHYP WITH GRANT OPTION ; 
      
    CREATE OR REPLACE VIEW B_L2USE1 ( 
    	DEPARTMENT , 
    	REP , 
    	SALES ) 
    	AS 
    	SELECT DEPARTMENT, REP, SALES  
    		 FROM           B_L1USE  
    		 WHERE DIVISION = '001'   
    	RCDFMT B_L2USE1   ; 
      
    LABEL ON TABLE B_L2USE1 
    	IS 'Sales for Region USE, Division 001' ; 
      
    LABEL ON COLUMN B_L2USE1 
    ( DEPARTMENT IS 'Department           ' , 
    	REP IS 'Rep                  ' , 
    	SALES IS 'Sales                ' ) ; 
      
    GRANT DELETE , SELECT , UPDATE   
    ON B_L2USE1 TO PUBLIC ; 
      
    GRANT ALTER , DELETE , REFERENCES , SELECT , UPDATE   
    ON B_L2USE1 TO TUOHYP WITH GRANT OPTION ; 
      
    CREATE OR REPLACE VIEW B_L2USE2 ( 
    	DEPARTMENT , 
    	REP , 
    	SALES ) 
    	AS 
    	SELECT DEPARTMENT, REP, SALES  
    		 FROM           B_L1USE  
    		 WHERE DIVISION = '002'   
    	RCDFMT B_L2USE2   ; 
      
    LABEL ON TABLE B_L2USE2 
    	IS 'Sales for Region USE, Division 002' ; 
      
    LABEL ON COLUMN B_L2USE2 
    ( DEPARTMENT IS 'Department           ' , 
    	REP IS 'Rep                  ' , 
    	SALES IS 'Sales                ' ) ; 
      
    GRANT DELETE , SELECT , UPDATE   
    ON B_L2USE2 TO PUBLIC ; 
      
    GRANT ALTER , DELETE , REFERENCES , SELECT , UPDATE   
    ON B_L2USE2 TO TUOHYP WITH GRANT OPTION ; 
      
    CREATE OR REPLACE VIEW B_L3USE1_S ( 
    	DEPARTMENT , 
    	SALES ) 
    	AS 
    	SELECT DEPARTMENT, SUM(SALES) AS SALES  
    		 FROM           B_L2USE1  
    		 GROUP BY DEPARTMENT   
    	RCDFMT B_L3USE1_S ; 
      
    LABEL ON TABLE B_L3USE1_S 
    	IS 'Sales for Region USE, Division 001, Summary' ; 
                                                                                
    LABEL ON COLUMN B_L3USE1_S 
    ( DEPARTMENT IS 'Department           ' ) ; 
                                                                                
    GRANT SELECT   
    ON B_L3USE1_S TO PUBLIC ; 
                                                                                
    GRANT ALTER , REFERENCES , SELECT   
    ON B_L3USE1_S TO TUOHYP WITH GRANT OPTION ; 
      
    CREATE OR REPLACE VIEW B_L3USE2_S ( 
    	DEPARTMENT , 
    	SALES ) 
    	AS 
    	SELECT DEPARTMENT, SUM(SALES) AS SALES  
    		 FROM           B_L2USE2  
    		 GROUP BY DEPARTMENT   
    	RCDFMT B_L3USE2_S ; 
                                                             
    LABEL ON TABLE B_L3USE2_S 
    	IS 'Sales for Region USE, Division 002, Summary' ; 
                                                                                
    LABEL ON COLUMN B_L3USE2_S 
    ( DEPARTMENT IS 'Department           ' ) ; 
                                                                                
    GRANT SELECT   
    ON B_L3USE2_S TO PUBLIC ; 
      
    GRANT ALTER , REFERENCES , SELECT   
    ON B_L3USE2_S TO TUOHYP WITH GRANT OPTION ; 
    

    But first, let’s look at what will be the two major components of this stored procedure:

    • Generating a recursive list of dependents
    • Generating SQL using the GENERATE_SQL_OBJECTS() Db2 for i Service

    Generating A Recursive List of Dependents

    This is the exact same concept as in the article quoted above, but we will be using a different syntax for generating the recursive list. The original stored procedure used a recursive Common Table Expression (CTE) but this stored procedure will use a hierarchical query instead. The exact same results, just a different syntax.

    This story contains code, which you can download here.

    All of the information we require is in the system catalog table QADBFDEP. We just need a recursive query to extract the information.

    An excellent description of using recursive queries is provided in the IBM Documentation (formerly the IBM Knowledge Center). At the time of writing this link worked. Or a search for “db2 for i using recursive queries” should get you there.

    The view B_L1USE has the following dependents:

    Both B_L1USE1 and B_L1USE2 are dependents of B_L1USE and they also have their own dependents. B_L1USE1_S is a dependent of B_L1US1 and B_L1USE2_S is a dependent of B_L1USE2.

    For the sake of comparison, this is the query from the original article that I would have used to generate the list:

    WITH RECURSIVE DEPENDENTS 
    ( CALLLEVEL, DBFFIL, DBFLIB, DBFFDP, DBFLDP ) AS 
    ( SELECT 1 AS CALLLEVEL, DBFFIL, DBFLIB, DBFFDP, DBFLDP 
        FROM QADBFDEP 
       WHERE (DBFFIL, DBFLIB) = ('B_L1USE', 'TESTSTUFF') 
      UNION ALL 
      SELECT CALLLEVEL + 1 AS CALLLEVEL, 
             PR.DBFFIL, PR.DBFLIB, PR.DBFFDP, PR.DBFLDP 
        FROM DEPENDENTS PH INNER JOIN QADBFDEP PR 
             ON (PH.DBFFDP, PH.DBFLDP) = (PR.DBFFIL, PR.DBFLIB) 
    ) 
    SELECT DISTINCT DBFFIL, DBFLIB, DBFFDP, DBFLDP 
       FROM DEPENDENTS 
       ORDER BY DBFFDP, DBFLDP ;
    

    Instead we will use this easier to read syntax to produce the list:

    SELECT CONNECT_BY_ROOT DBFFIL, CONNECT_BY_ROOT DBFLIB, DBFFDP, DBFLDP
      FROM QADBFDEP
      START WITH (DBFFIL, DBFLIB) = ('B_L1USE', 'TESTSTUFF')
      CONNECT BY PRIOR DBFFDP = DBFFIL AND PRIOR DBFLDP = DBFLIB
    ORDER BY DBFFDP , DBFLDP;
    

    The IBM Documentation page, referenced above, provides an excellent description of both techniques.

    The GENERATE_SQL_OBJECTS() Service

    The GENERATE_SQL_OBJECTS() service is a stored procedure that generates the SQL statements required to create the database objects listed in a table

    At the time of writing Run SQL Scripts does not provide an example for GENERATE_SQL_OBJECTS() but the procedure is described in detail in the IBM Documentation (https://www.ibm.com/docs/en/i/7.4?topic=services-generate-sql-objects-procedure).

    This is what we see if we prompt the GENERATE_SQL_OBJECTS() procedure:

    call QSYS2.GENERATE_SQL_OBJECTS(
                            SYSTEM_TABLE_NAME => , 
                            SYSTEM_TABLE_SCHEMA => , 
                            DATABASE_SOURCE_FILE_NAME => , 
                            DATABASE_SOURCE_FILE_LIBRARY_NAME => , 
                            DATABASE_SOURCE_FILE_MEMBER => , 
                            SEVERITY_LEVEL => , 
                            REPLACE_OPTION => , 
                            STATEMENT_FORMATTING_OPTION => , 
                            DATE_FORMAT => , 
                            DATE_SEPARATOR => , 
                            TIME_FORMAT => , 
                            TIME_SEPARATOR => , 
                            NAMING_OPTION => , 
                            DECIMAL_POINT => , 
                            STANDARDS_OPTION => , 
                            DROP_OPTION => , 
                            MESSAGE_LEVEL => , 
                            COMMENT_OPTION => , 
                            LABEL_OPTION => , 
                            HEADER_OPTION => , 
                            TRIGGER_OPTION => , 
                            CONSTRAINT_OPTION => , 
                            SYSTEM_NAME_OPTION => , 
                            PRIVILEGES_OPTION => , 
                            CCSID_OPTION => , 
                            CREATE_OR_REPLACE_OPTION => , 
                            OBFUSCATE_OPTION => , 
                            ACTIVATE_ROW_AND_COLUMN_ACCESS_CONTROL_OPTION => , 
                            MASK_AND_PERMISSION_OPTION => , 
                            QUALIFIED_NAME_OPTION => , 
                            ADDITIONAL_INDEX_OPTION => , 
                            INDEX_INSTEAD_OF_VIEW_OPTION => , 
                            TEMPORAL_OPTION => ,
                            SOURCE_STREAM_FILE => ,
    SOURCE_STREAM_FILE_END_OF_LINE => , SOURCE_STREAM_FILE_CCSID => );
    

    The main points to note are:

    • The first two parameters (SYSTEM_TABLE_NAME and SYSTEM_TABLE_SCHEMA) identify the table which contains the list of database object for which we will be generating SQL. (More about the format of this table in a moment.)
    • The next three parameters (DATABASE_SOURCE_FILE_NAME, DATABASE_SOURCE_FILE_LIBRARY_NAME and DATABASE_SOURCE_FILE_MEMBER) identify a source file member where the generated source is to be placed. These default to the member Q_GENSQOBJ in the source file Q_GENSQOBJ in QTEMP.
    • Providing a value of *STMF for DATABASE_SOURCE_FILE_NAME and providing appropriate values for SOURCE_STREAM_FILE, SOURCE_STREAM_FILE_END_OF_LINE and SOURCE_STREAM_FILE_CCSID means that the generated source will be placed in a file in the IFS instead of a member in a source physical file. The stream file parameters were introduced in IBM i 7.4 – TR4/IBM i 7.3 – TR10 Enhancements.
    • The rest of the parameters provide the standard Generate SQL options that allow you to customize, in some way, the DDL that is generated.

    The table which contains the list of database object for which we will be generating SQL must contain the following columns. (The table is described in detail in the GENERATE_SQL_OBJECTS() documentation.)

    object_schema varchar(258),
    object_name varchar(258),
    sql_object_type char(10)
    

    The GENERATE_SQL_FOR_DEPENDENTS Procedure

    The GENERATE_SQL_FOR_DEPENDENTS() procedure makes use of the two techniques described above to return a result set which is the generated DDL for the requested table/view and its dependents.

    These are the main points to note in the procedure. (Please refer to the call outs.)

    1. The parameters can be either the system table and schema names (10 characters each) or the SQL table and schema names
    2. Define a temporary table to contain the list of database object for which we will be generating SQL. Note that the with replace means that the table will be replaced if it already exists.
    3. If SQL names were provided as parameters get the corresponding system names. Or use the system names if they were provided.
    4. Add the requested table/view to the list of database objects for which DDL will be generated. Note the CASE statement to generate the required SQL object type.
    5. Return an error if the requested table/view was not found
    6. Add each of the dependents to the list of database objects for which DDL will be generated. This is basically the recursive query described above with the addition of a join to the QADBXREF table so we can determine the SQL object type. Note that the encoding (for the object type) in QADBXREF is different from that in SYSTABLES used in 4 above.
    7. Call the GENERATE_SQL_OBJECTS() procedure to generate the DDL. Since we do not specify otherwise, the DDL will be placed in the member Q_GENSQOBJ in the source file Q_GENSQOBJ in QTEMP. For customizing the DDL I am specifying that statements should be CREATE OR REPLACE, I do not want CCSIDs specified and I do not want qualified names.
    8. Declare and return a cursor over the generated source member.
        CREATE OR REPLACE PROCEDURE TESTSTUFF/GENERATE_SQL_FOR_DEPENDENTS 
    (1)   (IN P_DBNAME CHAR(10) DEFAULT '', 
           IN P_DBLIBRARY CHAR(10) DEFAULT '',
           IN P_SQL_DBNAME VARCHAR(256) DEFAULT '',
           IN P_SQL_SCHEMA VARCHAR(256) DEFAULT '')
                DYNAMIC RESULT SETS 1
                LANGUAGE SQL
                SPECIFIC TESTSTUFF/GENERATE_SQL_FOR_DEPENDENTS
                NOT DETERMINISTIC
                MODIFIES SQL DATA
                CALLED ON NULL INPUT
                PROGRAM TYPE SUB
                SET OPTION ALWBLK = *ALLREAD, ALWCPYDTA = *OPTIMIZE, COMMIT = *NONE,
                               DBGVIEW = *SOURCE, DECRESULT = (31, 31, 00), 
                               DFTRDBCOL = *NONE, DYNDFTCOL = *NO, DYNUSRPRF = *USER, 
                               SRTSEQ = *HEX
        BEGIN
        
        declare g_dbName char(10);
        declare g_dbLibrary char(10);
        declare g_local_sqlstate char(5);
        
        -- Create table for list of objects to be generated
    (2) declare global temporary table q_perf_ord (
                object_schema varchar(258),
                object_name varchar(258),
                sql_object_type char(10)
              )
            with replace;
        -- Get the system name of the requested object
        --  Caller can specify object by system name or SQL Name
        --  Procedure uses system name to retrieve dependents
    (3) if (p_dbName = '') then
          select system_table_name, system_table_schema
            into g_dbName, g_dbLibrary
            from systables 
            where (table_schema, table_name) = (p_SQL_Schema, p_SQL_DBName);
        else
          set g_dbName = p_dbName; 
          set g_dbLibrary = p_dbLibrary; 
        end if;  
        -- Add requested object to list of objects to be generated
    (4) insert into q_perf_ord    
        (
         select table_schema, table_name, 
          case when table_type in ('P', 'T') then 'TABLE'
               else 'VIEW'
               end
          from qsys2.systables 
          where (system_table_name, system_table_schema) = (g_dbName, g_dbLibrary)
        );
        -- Error if requested object not found
    (5) get diagnostics condition 1 g_local_sqlstate = returned_sqlstate;
        if (g_local_sqlstate = '02000') then
          signal sqlstate 'VV001' 
            set message_text = 'Table or view not found';
          return -1;
        end if;
        -- Add dependents to list of objects to be generated
    (6) insert Into q_perf_ord
            (select distinct dbfldp, dbxlfi,
                    case
                        when dbxatr = 'AL' Then 'ALIAS'
                        when dbxatr = 'IX' Then 'INDEX'
                        when dbxatr In ('TB', 'MQ', 'PF') Then 'TABLE'
                        when dbxatr In ('VW', 'LF') Then 'VIEW'
                        else dbxatr
                    end as object_type
                from (
                      select CONNECT_BY_ROOT dbffil, CONNECT_BY_ROOT dbflib, 
                             dbffdp, dbfldp, LEVEL As DEPENDENCY_LEVEL
                        from QADBFDEP
                       START WITH (dbffil, dbflib) = (g_dbName, g_dbLibrary)
                       CONNECT BY prior dbffdp = dbffil and prior dbfldp = dbflib
                     ) As OBJECTS
                     inner join QADBXREF
                         On (dbffdp, dbfldp) = (dbxfil, dbxlib));
        -- Generate SQL for list of objects                 
    (7)  call qsys2.generate_sql_objects(SYSTEM_TABLE_NAME => 'Q_PERF_ORD',  
                                        CREATE_OR_REPLACE_OPTION => '1', 
                                        CCSID_OPTION => '0',
                                        QUALIFIED_NAME_OPTION => '1');
        -- Set result set to generated source
          BEGIN
    (8)     declare list_cursor cursor with return to caller for 
              select SRCDTA from QTEMP.Q_GENSQOBJ;
            open list_cursor;
          END;
        
        END;
    

    If you prefer, instead of returning the DDL as a result set you could have parameters to identify a source member or IFS file, generate the DDL to that and then open it with Run SQL Scripts.

    I hope you find this tip as useful as I do!

    RELATED STORIES

    Find A View Of A View Of A View. . .

    IBM Documentation: Using Recursive Queries

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Tags: Tags: 400guru, DDL, FHG, Four Hundred Guru, IBM i, IFS, Run SQL Scripts, SQL

    Sponsored by
    Maxava

    Migrate IBM i with Confidence

    Tired of costly and risky migrations? Maxava Migrate Live minimizes disruption with seamless transitions. Upgrading to Power10 or cloud hosted system, Maxava has you covered!

    Learn More

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    As I See It: Orwellian PHP Decisions Loom as Original Distro Reaches End of Life

    One thought on “Guru: Generate SQL for Dependents”

    • Leslie Turner says:
      May 25, 2021 at 7:28 am

      Thanks Paul! The hierarchical query seems like a cleaner solution…. easier to read anyway.

      Reply

    Leave a Reply Cancel reply

TFH Volume: 31 Issue: 37

This Issue Sponsored By

  • Fresche Solutions
  • ProData
  • UCG Technologies
  • Raz-Lee Security
  • WorksRight Software

Table of Contents

  • A Million Miles Away From Machine Learning
  • PHP Decisions Loom as Original Distro Reaches End of Life
  • Guru: Generate SQL for Dependents
  • As I See It: Orwellian
  • COMMON Heads To Ohio With Hybrid NAViGATE Event

Content archive

  • The Four Hundred
  • Four Hundred Stuff
  • Four Hundred Guru

Recent Posts

  • POWERUp 2025 –Your Source For IBM i 7.6 Information
  • Maxava Consulting Services Does More Than HA/DR Project Management – A Lot More
  • Guru: Creating An SQL Stored Procedure That Returns A Result Set
  • As I See It: At Any Cost
  • IBM i PTF Guide, Volume 27, Number 19
  • IBM Unveils Manzan, A New Open Source Event Monitor For IBM i
  • Say Goodbye To Downtime: Update Your Database Without Taking Your Business Offline
  • i-Rays Brings Observability To IBM i Performance Problems
  • Another Non-TR “Technology Refresh” Happens With IBM i TR6
  • IBM i PTF Guide, Volume 27, Number 18

Subscribe

To get news from IT Jungle sent to your inbox every week, subscribe to our newsletter.

Pages

  • About Us
  • Contact
  • Contributors
  • Four Hundred Monitor
  • IBM i PTF Guide
  • Media Kit
  • Subscribe

Search

Copyright © 2025 IT Jungle