04 - System Administration04g - Varie sistemistica

Rimuovere i vecchi spool file per tenere sotto controllo il numero di lavori nel sistema (versione con procedura SQL)

Last Updated on 11 Settembre 2025 by Roberto De Pedrini

Nell’ultimo mio ultimo articolo “Rimuovere i vecchi spool file per tenere sotto controllo il numero di lavori nel sistema” ho pubblicato il sorgente di un programma CLLE che rimuove gli spooled file selezionati tramite JOBNAME, USERNAME, DAYS (Retention days), FILENAME, USRDTA e OUTQ in uso sui sistemi che amministro. Due lettori mi hanno fatto notare che lo stesso scopo può essere ottenuto utilizzano la procedura IBM DELETE_OLD_SPOOLED_FILES ma solo dopo averla personalizzata.

In questo articolo vediamo come modificare questa procedura SQL di IBM contenuta nella libreria SYSTOOLS aggiungendo i filtri mancanti e cioè JOBNAME, FILENAME e USRDTA come suggerito da IBM stessa:

Il sorgente SQL degli strumenti di Db2 for i forniti all’interno di SYSTOOLS può essere estratto e utilizzato come modello per costruire funzioni di aiuto simili o per creare una versione personalizzata all’interno di uno schema specificato dall’utente.

Prima di tutto dobbiamo recuperare il sorgente della procedura DELETE_OLD_SPOOLED_FILES e lo facciamo utilizzando un’altra procedura IBM chiamata GENERATE_SQL che si trova nella libreria QSYS2.

La procedura GENERATE_SQL genera le istruzioni del linguaggio di definizione dei dati SQL necessarie per ricreare un oggetto del database. I risultati vengono restituiti nel membro del file di origine del database specificato, nel file del flusso di origine o come set di risultati.

Da Run SQL scripts di Access Client Solutions generiamo lo stream file DELETE_OLD_SPOOLED_FILES.sql nella dir /tmp in IFS con questo statement SQL:

CALL
  QSYS2.GENERATE_SQL(
    DATABASE_OBJECT_NAME => 'DELETE_OLD_SPOOLED_FILES', 
    DATABASE_OBJECT_LIBRARY_NAME => 'SYSTOOLS', 
    DATABASE_OBJECT_TYPE => 'PROCEDURE', 
    DATABASE_SOURCE_FILE_NAME => '*STMF', 
    SOURCE_STREAM_FILE => '/tmp/DELETE_OLD_SPOOLED_FILES.sql',
    SOURCE_STREAM_FILE_CCSID => 1208,
    CREATE_OR_REPLACE_OPTION => 1,
    REPLACE_OPTION => '1'
  )
;

Avendo estratto il sorgente su di un file con CCSID 1208 (o 1252) possiamo editarlo da PC con Run SQL Scripts di Access Client Solutions o, se la dir dove si trova il file è stata condivisa con NetServer, con un qualunque editor di testo Windows.

Modifichiamo il sorgente come evidenziato sotto facendo attenzione a modificare la libreria di destinazione in modo che la procedura venga creata in una libreria utente.

In breve, le modifiche da apportare sono le seguenti:

  • modifica della libreria di destinazione da SYSTOOLS ad una vostra libreria utente;
  • definizione dei nuovi parametri P_JOBNAME, P_SPOOLED_FILE_NAME e P_USER_DATA;
  • dichiarazione della nuova variabile V_USER_DATA;
  • modifica dello statement WHERE aggiungendo il vincolo sui nuovi parametri;
  • inserimento della nuova variabile V_USER_DATA negli statement di FETCH;
  • modifica del commento e del commento sui parametri.

Se preferite, potete anche personalizzare il nome per far evidenziare ulteriormente la differenza con la procedura ufficiale.

--  Generazione SQL 
--  Versione:                  	V7R4M0 190621 
--  Generata su:               	05/09/25 15:47:40 
--  Database relazionale:      	TREBIRDB 
--  Opzioni standard:          	Db2 for i 
  
SET PATH "QSYS","QSYS2","SYSPROC","SYSIBMADM" ; 
  
CREATE OR REPLACE PROCEDURE MYLIB.DELETE_OLD_SPOOLED_FILES ( 
	IN DELETE_OLDER_THAN TIMESTAMP DEFAULT  ( CURRENT_TIMESTAMP - 3 MONTHS )  , 
	IN P_OUTPUT_QUEUE_LIBRARY_NAME VARCHAR(10) FOR SBCS DATA DEFAULT  '*ALL'  , 
	IN P_OUTPUT_QUEUE_NAME VARCHAR(10) FOR SBCS DATA DEFAULT  '*ALL'  , 
	IN P_USER_NAME VARCHAR(10) FOR SBCS DATA DEFAULT  '*ALL'  , 
	IN P_JOB_NAME VARCHAR(10) FOR SBCS DATA DEFAULT  '*ALL'  , 
	IN P_SPOOLED_FILE_NAME VARCHAR(10) FOR SBCS DATA DEFAULT  '*ALL'  , 
	IN P_USER_DATA VARCHAR(10) FOR SBCS DATA DEFAULT  '*ALL'  , 
	IN PREVIEW VARCHAR(3) FOR SBCS DATA DEFAULT  'NO'  ) 
	DYNAMIC RESULT SETS 1 
	LANGUAGE SQL 
	SPECIFIC MYLIB.DLTOLDSPL 
	NOT DETERMINISTIC 
	MODIFIES SQL DATA 
	CALLED ON NULL INPUT 
	SYSTEM_TIME SENSITIVE NO 
	SET OPTION  ALWBLK = *ALLREAD , 
	ALWCPYDTA = *OPTIMIZE , 
	COMMIT = *NONE , 
	DECRESULT = (31, 31, 00) , 
	DFTRDBCOL = QSYS2 , 
	DLYPRP = *NO , 
	DYNDFTCOL = *NO , 
	DYNUSRPRF = *USER , 
	SRTSEQ = *HEX , 
	USRPRF = *USER   
	BEGIN 
-- Example: PROCEDURE SYSTOOLS.DELETE_OLD_SPOOLED_FILES 
-- 
-- Disclaimer: 
-- This example is being provided by IBM to allow IBM i users to understand how to use 
-- SQL to manage spooled files. 
-- 
-- While efforts were made to verify the completeness and accuracy of this sample procedure, 
-- this sample is provided 'as is' without any warranty whatsoever and to the maximum extent permitted, 
-- IBM disclaims all implied warranties. 
-- 
-- Parameters: 
-- 1) DELETE_OLDER_THAN - A timestamp indicating to delete spooled files that were created BEFORE this point 
-- 5) PREVIEW - When set to YES, the list of spooled files that would be deleted are returned, but not deleted. 
--    If the value is not YES, all identified spooled files are deleted 
-- 
DECLARE NOT_FOUND CONDITION FOR '02000' ; 
DECLARE AT_END INTEGER DEFAULT 0 ; 
DECLARE V_SPOOLED_FILE_NAME VARCHAR ( 10 ) ; 
DECLARE V_JOB_NAME VARCHAR ( 28 ) ; 
DECLARE V_FILE_NUMBER BIGINT ; 
DECLARE V_USER_NAME VARCHAR ( 10 ) ; 
DECLARE V_USER_DATA VARCHAR ( 10 ) ; 
DECLARE V_CMDSTMT VARCHAR ( 200 ) ; 
DECLARE V_CRT_DATETIME TIMESTAMP ; 
DECLARE V_CRTDATE CHAR ( 15 ) ; 
DECLARE DATE_FMT CHAR ( 4 ) ; 
-- 
-- Find spooled files to delete 
-- 
DECLARE V_SPOOLED_FILE_STMT_TEXT VARCHAR ( 3000 ) DEFAULT 
'SELECT SPOOLED_FILE_NAME, JOB_NAME, FILE_NUMBER, USER_NAME, CREATE_TIMESTAMP, USER_DATA
  FROM QSYS2.OUTPUT_QUEUE_ENTRIES_BASIC
  WHERE CREATE_TIMESTAMP < ?' ; 
  
DECLARE SPOOLED_FILE_CURSOR CURSOR WITH RETURN TO CALLER FOR V_SPOOLED_FILE_STMT ; 
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET AT_END = 1 ; 
DECLARE CONTINUE HANDLER FOR NOT_FOUND SET AT_END = 1 ; 
  
IF ( PREVIEW = 'YES' ) THEN 
SET V_SPOOLED_FILE_STMT_TEXT = 
'SELECT SPOOLED_FILE_NAME, JOB_NAME, FILE_NUMBER, USER_NAME,
        SIZE, OUTPUT_QUEUE_NAME, OUTPUT_QUEUE_LIBRARY_NAME, CREATE_TIMESTAMP,
        USER_DATA, STATUS, TOTAL_PAGES, COPIES, FORM_TYPE,
        DEVICE_TYPE, OUTPUT_PRIORITY, SYSTEM
  FROM QSYS2.OUTPUT_QUEUE_ENTRIES_BASIC
  WHERE CREATE_TIMESTAMP < ?' ; 
END IF ; 
  
IF ( P_OUTPUT_QUEUE_LIBRARY_NAME <> '*ALL' ) THEN 
SET V_SPOOLED_FILE_STMT_TEXT = V_SPOOLED_FILE_STMT_TEXT CONCAT 
' AND OUTPUT_QUEUE_LIBRARY_NAME =''' CONCAT P_OUTPUT_QUEUE_LIBRARY_NAME CONCAT '''' ; 
END IF ; 
  
IF ( P_OUTPUT_QUEUE_NAME <> '*ALL' ) THEN 
SET V_SPOOLED_FILE_STMT_TEXT = V_SPOOLED_FILE_STMT_TEXT CONCAT 
' AND OUTPUT_QUEUE_NAME =''' CONCAT P_OUTPUT_QUEUE_NAME CONCAT '''' ; 
END IF ; 
  
IF ( P_USER_NAME <> '*ALL' ) THEN 
SET V_SPOOLED_FILE_STMT_TEXT = V_SPOOLED_FILE_STMT_TEXT CONCAT 
' AND USER_NAME =''' CONCAT P_USER_NAME CONCAT '''' ; 
END IF ; 
  
IF ( P_JOB_NAME <> '*ALL' ) THEN 
SET V_SPOOLED_FILE_STMT_TEXT = V_SPOOLED_FILE_STMT_TEXT CONCAT 
' AND REGEXP_SUBSTR(JOB_NAME, ''[^/]+$'') =''' CONCAT P_JOB_NAME CONCAT '''' ; 
END IF ; 
  
IF ( P_SPOOLED_FILE_NAME <> '*ALL' ) THEN 
SET V_SPOOLED_FILE_STMT_TEXT = V_SPOOLED_FILE_STMT_TEXT CONCAT 
' AND SPOOLED_FILE_NAME =''' CONCAT P_SPOOLED_FILE_NAME CONCAT '''' ; 
END IF ; 
  
IF ( P_USER_DATA <> '*ALL' ) THEN 
SET V_SPOOLED_FILE_STMT_TEXT = V_SPOOLED_FILE_STMT_TEXT CONCAT 
' AND USER_DATA =''' CONCAT P_USER_DATA CONCAT '''' ; 
END IF ; 
  
PREPARE V_SPOOLED_FILE_STMT FROM V_SPOOLED_FILE_STMT_TEXT ; 
OPEN SPOOLED_FILE_CURSOR USING DELETE_OLDER_THAN ; 
IF ( PREVIEW = 'YES' ) THEN 
RETURN ; 
END IF ; 
SELECT DATE_FORMAT INTO DATE_FMT 
FROM TABLE ( QSYS2 . ACTIVE_JOB_INFO ( JOB_NAME_FILTER => '*' , DETAILED_INFO => 'ALL' ) ) ; 
FETCH FROM SPOOLED_FILE_CURSOR INTO V_SPOOLED_FILE_NAME , V_JOB_NAME , V_FILE_NUMBER , V_USER_NAME , V_CRT_DATETIME , V_USER_DATA ; 
  
WHILE ( AT_END = 0 ) DO 
-- 
-- Add 500 microseconds to keep the identical date and time value with spool file's. 
-- 
SET V_CRT_DATETIME = V_CRT_DATETIME + 500 MICROSECONDS ; 
-- 
-- Convert date and time ISO format to current job's date and time format 
-- 
IF DATE_FMT = '*DMY' THEN 
SET V_CRTDATE = VARCHAR_FORMAT ( V_CRT_DATETIME , 'DDMMYYYY HH24MISS' ) ; 
ELSEIF DATE_FMT = '*JUL' THEN 
SET V_CRTDATE = VARCHAR_FORMAT ( V_CRT_DATETIME , 'YYYYDDD HH24MISS' ) ; 
ELSEIF DATE_FMT = '*MDY' THEN 
SET V_CRTDATE = VARCHAR_FORMAT ( V_CRT_DATETIME , 'MMDDYYYY HH24MISS' ) ; 
ELSEIF DATE_FMT = '*YMD' THEN 
SET V_CRTDATE = VARCHAR_FORMAT ( V_CRT_DATETIME , 'YYYYMMDD HH24MISS' ) ; 
END IF ; 
SET V_CMDSTMT = 'QSYS/DLTSPLF     FILE(' CONCAT V_SPOOLED_FILE_NAME CONCAT 
') JOB(' CONCAT V_JOB_NAME CONCAT 
') SPLNBR(' CONCAT V_FILE_NUMBER CONCAT 
') CRTDATE(' CONCAT V_CRTDATE CONCAT 
') JOBSYSNAME(*ANY)  SELECT(' CONCAT V_USER_NAME CONCAT ')' ; 
CALL QSYS2 . QCMDEXC ( V_CMDSTMT ) ; 
FETCH FROM SPOOLED_FILE_CURSOR INTO V_SPOOLED_FILE_NAME , V_JOB_NAME , V_FILE_NUMBER , V_USER_NAME , V_CRT_DATETIME , V_USER_DATA ; 
END WHILE ; 
  
CLOSE SPOOLED_FILE_CURSOR ; 
END  ; 
  
COMMENT ON SPECIFIC PROCEDURE MYLIB.DLTOLDSPL 
	IS 'USER VERSION CREATED FROM DB2 FOR IBM i SUPPLIED OBJECT VERSION 07400110003' ; 
  
COMMENT ON PARAMETER SPECIFIC PROCEDURE MYLIB.DLTOLDSPL 
( DELETE_OLDER_THAN IS 'timestamp - Default: CURRENT TIMESTAMP - 3 MONTHS' , 
	P_OUTPUT_QUEUE_LIBRARY_NAME IS 'name, *ALL - Default: *ALL' , 
	P_OUTPUT_QUEUE_NAME IS 'name, *ALL - Default: *ALL' , 
	P_USER_NAME IS 'name, *ALL - Default: *ALL' , 
	P_JOB_NAME IS 'name, *ALL - Default: *ALL' , 
	P_SPOOLED_FILE_NAME IS 'name, *ALL - Default: *ALL' , 
	P_USER_DATA IS 'name, *ALL - Default: *ALL' , 
	PREVIEW IS 'NO, YES - Default: NO' ) ; 
  
GRANT EXECUTE   
ON SPECIFIC PROCEDURE MYLIB.DLTOLDSPL 
TO PUBLIC ; 
  
GRANT ALTER , EXECUTE   
ON SPECIFIC PROCEDURE MYLIB.DLTOLDSPL 
TO QSYS WITH GRANT OPTION ;

A questo punto siamo pronti a creare la nostra procedura SQL.

Possiamo farlo direttamente da QShell o da PASE con il comando:

db2 -tvf /tmp/DELETE_OLD_SPOOLED_FILES.sql

Oppure, richiamando lo stesso comando da riga comandi:

QSH CMD('db2 -tvf /tmp/DELETE_OLD_SPOOLED_FILES.sql')

Dove:

  • -t = usa “;” come terminatore dello statement
  • -v = verbose (stampa gli statement prima di eseguirli)
  • -f = file di input

Oppure possiamo più comodamente aprire il file ed eseguirlo direttamente da Run SQL Scripts di Access Client Solutions.

Una volta che la procedura SQL è disponibile possiamo provarla. Di seguito trovate un esempio di chiamata che non cancella nulla ma mostra solo il result set (parametro PREVIEW => ‘YES’):

CALL
  MYLIB.DELETE_OLD_SPOOLED_FILES(
    DELETE_OLDER_THAN => CURRENT DATE - 60 DAYS, 
    P_OUTPUT_QUEUE_LIBRARY_NAME => 'QUSRSYS', -- '*ALL'
    P_OUTPUT_QUEUE_NAME => 'QEZJOBLOG',       -- '*ALL'
    P_USER_NAME => 'QUSER',                   -- '*ALL'
    P_JOB_NAME => 'QRWTSRVR',                 -- '*ALL'
    P_SPOOLED_FILE_NAME => 'QPJOBLOG',        -- '*ALL'
    P_USER_DATA => 'QRWTSRVR',                -- '*ALL'
    PREVIEW => 'YES')
;

Per rimuovere la procedura basta eseguire questa istruzione SQL:

DROP PROCEDURE
  MYLIB.DELETE_OLD_SPOOLED_FILES
;

A questo punto, il programma CLLE di esempio fornito nel precedente articolo diventa:

/* Before compiling: +
   CREATE TABLE QTEMP.SQLOUT AS ( +
   SELECT TOTAL_JOBS_IN_SYSTEM, MAXIMUM_JOBS_IN_SYSTEM +
   FROM QSYS2.SYSTEM_STATUS_INFO_BASIC) +
   WITH NO DATA */

             PGM

             DCLF       FILE(SQLOUT) OPNID(SQLOUT) ALWVARLEN(*YES)

             DCL        VAR(&E) TYPE(*LGL) VALUE('0')
             DCL        VAR(&ERROR) TYPE(*LGL) VALUE('0')

/* RUNSQL */
             DCL        VAR(&SQL) TYPE(*CHAR) LEN(5000)
             DCL        VAR(&OUTQ) TYPE(*CHAR) LEN(21)
             DCL        VAR(&SQLLIB) TYPE(*CHAR) LEN(10) VALUE('QTEMP')
             DCL        VAR(&SQLFIL) TYPE(*CHAR) LEN(10) VALUE('SQLOUT')
             DCL        VAR(&SQLMBR) TYPE(*CHAR) LEN(10) VALUE('SQLOUT')

             MONMSG     MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR))

             CALLSUBR   SUBR(RTVTOTJOBS)

             RUNSQL     SQL('CALL MYLIB.DELETE_OLD_SPOOLED_FILES(+
                          DELETE_OLDER_THAN => CURRENT DATE - 7 DAYS, +
                          P_USER_NAME => ''*ALL'', +
                          P_JOB_NAME => ''QP0ZSPWT'')') +
                          COMMIT(*NONE)
             MONMSG     MSGID(SQL0000) EXEC(CHGVAR VAR(&E) VALUE('1'))

             RUNSQL     SQL('CALL MYLIB.DELETE_OLD_SPOOLED_FILES(+
                          DELETE_OLDER_THAN => CURRENT DATE - 7 DAYS, +
                          P_USER_NAME => ''*ALL'', +
                          P_JOB_NAME => ''QP0ZSPWP'')') +
                          COMMIT(*NONE)
             MONMSG     MSGID(SQL0000) EXEC(CHGVAR VAR(&E) VALUE('1'))

             RUNSQL     SQL('CALL MYLIB.DELETE_OLD_SPOOLED_FILES(+
                          DELETE_OLDER_THAN => CURRENT DATE - 7 DAYS, +
                          P_USER_NAME => ''*ALL'', +
                          P_JOB_NAME => ''QJVACMDSRV'')') +
                          COMMIT(*NONE)
             MONMSG     MSGID(SQL0000) EXEC(CHGVAR VAR(&E) VALUE('1'))

             RUNSQL     SQL('CALL MYLIB.DELETE_OLD_SPOOLED_FILES(+
                          DELETE_OLDER_THAN => CURRENT DATE - 7 DAYS, +
                          P_USER_NAME => ''*ALL'', +
                          P_JOB_NAME => ''QZSHSH'')') +
                          COMMIT(*NONE)
             MONMSG     MSGID(SQL0000) EXEC(CHGVAR VAR(&E) VALUE('1'))

             RUNSQL     SQL('CALL MYLIB.DELETE_OLD_SPOOLED_FILES(+
                          DELETE_OLDER_THAN => CURRENT DATE - 7 DAYS, +
                          P_USER_NAME => ''QUSER'', +
                          P_JOB_NAME => ''QRWTSRVR'')') +
                          COMMIT(*NONE)
             MONMSG     MSGID(SQL0000) EXEC(CHGVAR VAR(&E) VALUE('1'))

             RUNSQL     SQL('CALL MYLIB.DELETE_OLD_SPOOLED_FILES(+
                          DELETE_OLDER_THAN => CURRENT DATE - 7 DAYS, +
                          P_USER_NAME => ''*ALL'', +
                          P_JOB_NAME => ''QPRTJOB'', +
                          P_USER_DATA => ''QZRCSRVS'')') +
                          COMMIT(*NONE)
             MONMSG     MSGID(SQL0000) EXEC(CHGVAR VAR(&E) VALUE('1'))

             CALLSUBR   SUBR(RTVTOTJOBS)

             GOTO       CMDLBL(ENDCLP)

 ERROR:
             CHGVAR     VAR(&ERROR) VALUE('1')
             MONMSG     MSGID(CPF0000)
             GOTO       CMDLBL(ENDCLP)

 ENDCLP:
             IF         COND(&ERROR) THEN(DO)
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +
                          MSGDTA('Rilevato errore durante +
                          l''esecuzione della procedura, vedere +
                          messaggi precedenti') MSGTYPE(*ESCAPE)
             MONMSG     MSGID(CPF0000)
             ENDDO
             ELSE       CMD(IF COND(&E) THEN(DO))
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +
                          MSGDTA('Spooled files rimossi +
                          parzialmente, vedere messaggi +
                          precedenti') MSGTYPE(*ESCAPE)
             MONMSG     MSGID(CPF0000)
             ENDDO

             RETURN

 RTVTOTJOBS: SUBR       SUBR(RTVTOTJOBS)

                DLTF       FILE(&SQLLIB/&SQLFIL)
                MONMSG     MSGID(CPF2105)

                CHGVAR     VAR(&SQL) VALUE('SELECT TOTAL_JOBS_IN_SYSTEM, +
                             MAXIMUM_JOBS_IN_SYSTEM FROM +
                             QSYS2.SYSTEM_STATUS_INFO_BASIC')
                CHGVAR     VAR(&SQL) VALUE('CREATE TABLE' *BCAT &SQLLIB +
                             *TCAT '/' *CAT &SQLFIL *BCAT 'AS (' *CAT &SQL +
                             *TCAT ') WITH DATA')

                RUNSQL     SQL(&SQL) COMMIT(*NONE) NAMING(*SYS)
                MONMSG     MSGID(SQL0000) EXEC(DO)
                ENDDO

                OVRDBF     FILE(SQLOUT) TOFILE(&SQLLIB/&SQLFIL) +
                             MBR(&SQLMBR) LVLCHK(*NO) OVRSCOPE(*CALLLVL)

                RCVF       OPNID(SQLOUT)

                CLOSE      OPNID(SQLOUT) /* RVCF */

                DLTOVR     FILE(SQLOUT) LVL(*) /* OVRDBF */

                SNDPGMMSG  MSGID(CPI8859) MSGF(QCPFMSG) MSGDTA('Jobs in +
                             system:' *BCAT %CHAR(&SQLOUT_TOTAL_JOBS) +
                             *BCAT '/' *BCAT %CHAR(&SQLOUT_MAX_JOBS)) +
                             TOPGMQ(*PRV) TOMSGQ(*TOPGMQ) MSGTYPE(*INFO)

                DLTF       FILE(&SQLLIB/&SQLFIL)
                MONMSG     MSGID(CPF0000)

             ENDSUBR    RTNVAL(0)

 ENDPGM:
             ENDPGM

Riferimenti

Related Posts
DB2 for i SQL – Stringhe – POSSTR-LOCATE-LOCATE_IN_STRING (IT)

Introduzione Spesso, nelle nostre applicazioni, abbiamo la necessità di lavorare con le stringhe di testo e l'SQL del DB2 può Read more

DB2 for i & SQL – FAQ & Howto (Part. 1) (IT)

Database DB2 e SQL ... forse lo strumento più potente e completo che abbiamo sulla piattaforma IBM i: ecco una Read more

Annuncio IBM i 7.4

Arriva direttamente con l'uovo di Pasqua questo annuncio IBM per le novità della versione IBM i 7.4, versione iNext secondo Read more

Generated Always Columns – Approfondimenti (IT)

Introduzione "Generated Always Column": sono colonne, campi, di una tabella il cui contenuto è controllato direttamente dal sistema ... e Read more

About author

Amministratore sistemi IBM i

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *