…a volte è utile e necessario.
Quali paragoni? Confrontare il contenuto di due librerie, di due cartelle IFS, di due tabelle o di due stream files.
Esistono vari modi per confrontare. Ci concentreremo in particolare sui metodi che sfruttano istruzioni SQL, ma non solo.
Vediamo in questo articolo come confrontare:
- contenuto di due tabelle di DB2
- 1) funzione hash_row
- 2) funzione compare_file
- 3) operatori insiemistici
- 4) metodo Scott Forstie
- lista degli oggetti contenuti in due librerie
- 1) funzione object_statistics
- 2) vista systables
- contenuto e proprietà di due stream file
- 1) funzione hash*
- 2) funzione compare_ifs
- 3) comandi PASE
- lista degli stream files contenuti in due cartelle
- 1) funzione ifs_object_statistics
- 2) funzione compare_ifs
- 3) comandi PASE
Confrontare due tabelle
Tabelle di esempio:


Metodo 1: funzione HASH_ROW
Nel DB2 esistono alcune funzioni per calcolare l’hash con diversi algoritmi: HASH_MD5, HASH_SHA1, HASH_SHA256, and HASH_SHA512. Dalla versione IBM i 7.4 TR2 è stata introdotta la funzione HASH_ROW, che restituisce l’hash calcolato con algoritmo SHA512 di un record di una tabella. Per identificare quali siano i record diversi a parità di chiave primaria tra due tabelle con identico tracciato record ma potenzialmente contenuto differente, è sufficiente calcolare l’hash di ogni record e poi confrontarlo.
Nell’esempio la tabella BANCOMAT ha definito come chiave primaria il campo BANCOD che viene utilizzato nella clausola di join per abbinare i record corrispondenti della tabella 1 con la tabella 2. In questo modo anche se i record nelle due tabelle sono in posizioni RRN differenti, il risultato del confronto non è inficiato.
with TAB1 as (select BANCOD as ROW_KEY, hash_row(T) as ROW_VALUE, rid(T) as RRN from MR731.BANCOMAT as T), TAB2 as (select BANCOD as ROW_KEY, hash_row(T) ROW_VALUE, rid(T) as RRN from MR732.BANCOMAT T) select coalesce(T1.ROW_KEY, T2.ROW_KEY) as ROW_KEY, T1.RRN as T1_RRN, T2.RRN as T2_RRN from TAB1 as T1 full outer join TAB2 as T2 on T1.ROW_KEY = T2.ROW_KEY where T1.ROW_VALUE is distinct from T2.ROW_VALUE;
Metodo 2: funzione COMPARE_FILE
Da IBM i 7.4 TR2 e 7.3 TR8 è disponibile la funzione di tabella COMPARE_FILE specifica per eseguire la comparazione di due tabelle. Non solo per il contenuto ma anche per gli attributi di definizione.
I parametri COMPARE_ATTRIBUTES
e COMPARE_DATA
consentono di scegliere il tipo di confronto che si desidera eseguire: attributi di definizione e/o contenuto dei record. In entrambi i parametri è possibile scegliere un confronto rapido 'QUICK'
o dettagliato 'YES'
.
Molto interessante il parametro RDB2
che consente di specificare per la seconda tabella il nome del db remoto.
Bisogna porre attenzione a due caratteristiche di questa funzione:
- la corrispondenza dei record tra le due tabelle è basato su RRN, quindi anche se il contenuto di due file è identico ma i record hanno rrn differenti saranno considerati diversi
- non è possibile confrontare un file fisico creato con le DDS e una tabella creata con SQL DDL
Vediamo alcuni esempi sempre utilizzando le tabelle di esempio BANCOMAT:
select * from table(COMPARE_FILE(LIBRARY1 => 'MR731', FILE1 => 'BANCOMAT', LIBRARY2 => 'MR732', FILE2 => 'BANCOMAT', COMPARE_ATTRIBUTES => 'QUICK', COMPARE_DATA => 'QUICK' ) );
select * from table(COMPARE_FILE(LIBRARY1 => 'MR731', FILE1 => 'BANCOMAT', LIBRARY2 => 'MR732', FILE2 => 'BANCOMAT', COMPARE_ATTRIBUTES => 'YES', COMPARE_DATA => 'NO' ) );
Nota: la libreria MR731 è stata creata con istruzione SQL CREATE SCHEMA e quindi le tabelle sono per default registrate su giornale, invece la libreria MR732 è stata creata con CRTLIB e per la tabella BANCOMAT non sono state eseguite le istruzioni di creazione della chiave primaria e dei vincoli.
select * from table(COMPARE_FILE(LIBRARY1 => 'MR731', FILE1 => 'BANCOMAT', LIBRARY2 => 'MR732', FILE2 => 'BANCOMAT', COMPARE_ATTRIBUTES => 'NO', COMPARE_DATA => 'YES' ) ) order by FILE1;
Per utilizzare questa funzione senza scrivere l’istruzione SQL è possibile sfruttare la gestione schemi di ACS.

Dalla lista delle tabelle aprire il menu contestuale selezionando la prima tabella che si desidera confrontare. Quindi visualizzare la lista in cui è presente la seconda tabella e dal menu contestuale scegliere la voce “Confronta con…”

A questo punto scegliere le opzioni di confronto che corrispondono ai parametri COMPARE_ATTRIBUTES e COMPARE_DATA della funzione SQL COMPARE_FILE.

COMPARE_FILE per confrontare file sorgenti
I file sorgenti sono tabelle “particolari”. La funzione COMPARE_FILE
consente anche di confrontare due file sorgenti (PF-SRC).
Prendiamo ad esempio i due file sorgenti QTOOLS
nelle librerie MR731
e MR732


E proviamo a confrontarli
select * from table(COMPARE_FILE(LIBRARY1 => 'MR731', FILE1 => 'QTOOLS', LIBRARY2 => 'MR732', FILE2 => 'QTOOLS', COMPARE_ATTRIBUTES => 'YES', COMPARE_DATA => 'NO' ) );
Metodo 3: operatori insiemistici
Un altro metodo per scrivere un’istruzione SQL che confronti il contenuti di due tabelle è utilizzare gli operatori insiemistici EXCEPT e UNION.
select 'MR731 vs MR732' as "Confronto", T1.* from MR731.BANCOMAT as T1 except select 'MR731 vs MR732' as "Confronto", T2.* from MR732.BANCOMAT as T2 union all select 'MR732 vs MR731' as "Confronto", T2.* from MR732.BANCOMAT as T2 except select 'MR732 vs MR731' as "Confronto", T1.* from MR731.BANCOMAT as T1;
Metodo 4: procedura GENERATE_FILE_COMPARE
Vi suggerisco di consultare questo gist di Scott Forstie.
Nel gist trovate le istruzioni SQL per creare la procedura GENERATE_FILE_COMPARE
. Una volta creata la procedura la potete eseguire per ottenere un’istruzione SQL di confronto del contenuto di due tabelle.
Sempre utilizzando come esempio le due tabelle BANCOMAT
possiamo eseguire la procedura con questa istruzione
values GENERATE_FILE_COMPARE( SOURCE_SCHEMA => 'MR731', SOURCE_TABLE => 'BANCOMAT', TARGET_SCHEMA => 'MR732', TARGET_TABLE => 'BANCOMAT');
Il risultato sarà questa istruzione
select 'MR731.BANCOMAT' as table, rrn(a) as rrn, a.* from MR731.BANCOMAT a left exception join MR732.BANCOMAT b on A.BANCOD IS NOT DISTINCT FROM B.BANCOD AND A.BANDESC IS NOT DISTINCT FROM B.BANDESC AND A.BANADDR IS NOT DISTINCT FROM B.BANADDR AND A.BANCITY IS NOT DISTINCT FROM B.BANCITY AND A.BANPROV IS NOT DISTINCT FROM B.BANPROV AND A.BANZIP IS NOT DISTINCT FROM B.BANZIP AND A.BANLOC IS NOT DISTINCT FROM B.BANLOC UNION ALL select 'MR732.BANCOMAT', rrn(b), b.* from MR731.BANCOMAT a right exception join MR732.BANCOMAT b on A.BANCOD IS NOT DISTINCT FROM B.BANCOD AND A.BANDESC IS NOT DISTINCT FROM B.BANDESC AND A.BANADDR IS NOT DISTINCT FROM B.BANADDR AND A.BANCITY IS NOT DISTINCT FROM B.BANCITY AND A.BANPROV IS NOT DISTINCT FROM B.BANPROV AND A.BANZIP IS NOT DISTINCT FROM B.BANZIP AND A.BANLOC IS NOT DISTINCT FROM B.BANLOC order by 2;
Eseguendola otterremo come risultato il confronto delle tue tabelle BANCOMAT
Confrontare due librerie
Metodo 1: funzione OBJECT_STATISTICS
Sfruttando la funzione di tabella OBJECT_STATISTICS possiamo costruire delle istruzioni SQL per confrontare il contenuto di due librerie, eventualmente presenti anche su sistemi remoti.
Queste istruzioni sono presenti nello script Query di tutti i giorni.
-- [B06a] confronto lista oggetti tra due librerie with LIB1 as (select 'L1' as PROV, coalesce(OBJLONGNAME, OBJNAME) as OBJNAME, OBJTYPE, OBJATTRIBUTE, OBJTEXT, CHANGE_TIMESTAMP from table(QSYS2.OBJECT_STATISTICS(OBJECT_SCHEMA => :Libreria1, OBJTYPELIST => '*ALL')) as L --where ), LIB2 as (select 'L2' as PROV, coalesce(OBJLONGNAME, OBJNAME) as OBJNAME, OBJTYPE, OBJATTRIBUTE, OBJTEXT, CHANGE_TIMESTAMP from table(QSYS2.OBJECT_STATISTICS(OBJECT_SCHEMA => :Libreria2, OBJTYPELIST => '*ALL')) as L --where ) select coalesce(LIB1.OBJNAME, LIB2.OBJNAME) as "Oggetto", coalesce(LIB1.OBJTYPE, LIB2.OBJTYPE) as "Tipo", coalesce(LIB1.OBJATTRIBUTE, LIB2.OBJATTRIBUTE) as "Attr.", LIB1.PROV, LIB1.OBJTEXT "Lib1 descrizione", timestamp(LIB1.CHANGE_TIMESTAMP, 0) as "Lib1 Data/ora mod.", LIB2.PROV, LIB2.OBJTEXT "Lib2 descrizione", timestamp(LIB2.CHANGE_TIMESTAMP, 0) as "Lib2 Data/ora mod." from LIB1 -- oggetti esistenti in entrambe le librerie --inner join LIB2 -- oggetti mancanti in Libreria1 rispetto a Libreria2 --right join LIB2 -- oggetti mancanti in Libreria2 rispetto a Libreria1 --exception join LIB2 -- confronto completo full outer join LIB2 on LIB1.OBJNAME = LIB2.OBJNAME and LIB1.OBJTYPE = LIB2.OBJTYPE -- oggetti mancanti in Libreria1 rispetto a Libreria2 --where LIB1.OBJNAME is null order by 1, 2;
Questa istruzione SQL sfrutta la sintassi delle CTE (Common Table Expression) per creare due risultati temporanei LIB1 e LIB2 che contengono la lista degli oggetti della libreria 1 e della libreria 2 che si desidera confrontare. Se si desidera restringere il confronto solo ad un sottoinsieme di oggetti è possibile modificare i parametri della funzione di tabella OBJECT_STATISTICS o la clausola where delle sottoquery che definiscono LIB1/LIB2.
Una volta che si ha a disposizione l’elenco restituito da LIB1 e LIB2 si può operare il confronto con diverse tipologie di clausole join in base al risultato che si desidera ottenere:
- lista oggetti presenti in entrambe le librerie: clausola di inner join eventualmente specificando nella clausola where regole di confronto su uno o più attributi degli oggetti (data creazione, dimensione, ecc.)
- lista oggetti mancanti nella libreria 1 rispetto alla libreria 2: clausola right join unitamente alla clausola where per filtrare solo i record con LIB1.OBJNAME nullo
- lista oggetti mancanti nella libreria 2 rispetto alla libreria 1: clausola exception join
- confronto completo tra libreria 1 e libreria 2: clausola full outer join
Con un’istruzione simile è possibile confrontare il contenuto di due librerie presenti su sistemi diversi. In questo caso occorre creare su uno dei due sistemi (lo chiameremo locale) una tabella temporanea con il risultato della funzione di tabella OBJECT_STATISTICS eseguita sul sistema remoto.
-- [B06b] confronto lista oggetti tra due librerie su sistemi remoti -- step 1) creare in locale una tabella temporanea con la lista oggetti della libreria remota -- se utente/password locali non coincidono con utente/password remoti occorre... -- ...o connettersi esplicitamente al sistema remoto --connect to OTHERSYS user REMUSER using 'REMPWD'; -- ...o aggiungere una voce di autorizzazione server per il nome server DRDA RDB NAME (N.B. il valore di sistema QRETSVRSEC deve essere uguale a 1) -- ADDSVRAUTE USRPRF(LOCUSER) SERVER(OTHERSYS) USRID(REMUSER) PASSWORD(REMPWD) create table QTEMP.LIB_REM as (select 'LR' as PROV, coalesce(OBJLONGNAME, OBJNAME) as OBJNAME, OBJTYPE, OBJATTRIBUTE, OBJTEXT, CHANGE_TIMESTAMP from remote table(OTHERSYS.QSYS2.OBJECT_STATISTICS(OBJECT_SCHEMA => :LibreriaRem, OBJTYPELIST => '*ALL')) as L --where ) with data;
A questo punto si può costruire una query di confronto del tutto simile all’esempio precedente
-- step 2 confronto libreria locale con la lista ottenuta dal sistema remoto with LIBC as (select 'LC' as PROV, coalesce(OBJLONGNAME, OBJNAME) as OBJNAME, OBJTYPE, OBJATTRIBUTE, OBJTEXT, CHANGE_TIMESTAMP from table(QSYS2.OBJECT_STATISTICS(OBJECT_SCHEMA => :LibreriaLoc, OBJTYPELIST => '*ALL')) as L --where ), LIBR as (select * from QTEMP.LIB_REM --where ) select coalesce(LIBC.OBJNAME, LIBR.OBJNAME) as "Oggetto", coalesce(LIBC.OBJTYPE, LIBR.OBJTYPE) as "Tipo", coalesce(LIBC.OBJATTRIBUTE, LIBR.OBJATTRIBUTE) as "Attr.", LIBC.PROV, LIBC.OBJTEXT "Lib.loc. descrizione", timestamp(LIBC.CHANGE_TIMESTAMP, 0) as "Lib.loc. Data/ora mod.", LIBR.PROV, LIBR.OBJTEXT "Lib.rem. descrizione", timestamp(LIBR.CHANGE_TIMESTAMP, 0) as "Lib.rem. Data/ora mod." from LIBC -- oggetti esistenti in entrambe le librerie --inner join LIBR -- oggetti mancanti in Libreria1 rispetto a Libreria2 --right join LIBR -- oggetti mancanti in Libreria2 rispetto a Libreria1 --exception join LIBR -- confronto completo full outer join LIBR on LIBC.OBJNAME = LIBR.OBJNAME and LIBC.OBJTYPE = LIBR.OBJTYPE -- oggetti mancanti in Libreria1 rispetto a Libreria2 --where LIBC.OBJNAME is null order by 1, 2;
Metodo 2: vista SYSTABLES del catalogo DB2
Nel caso in cui il confronto tra le due librerie è limitato ai soli file fisici/logici si può evitare l’uso della funzione OBJECT_STATISTICS e consultare direttamente la vista SYSTABLES del catalogo DB2.
Queste istruzioni sono presenti nello script Query di tutti i giorni.
Confronto rapido basato sul conteggio degli oggetti presenti nelle due librerie:
-- [F14] confronto conteggio oggetti database tra due librerie with LIB1 as (select DESCRIPTION, count(*) as NUM from SYSTABLES inner join (values ('A', 'ALIAS'), ('L', 'LOGICAL FILE'), ('M', 'MQT'), ('P', 'TABLE'), ('T', 'TABLE'), ('V', 'VIEW')) as TYPE(CODE, DESCRIPTION) on TABLE_TYPE = TYPE.CODE where FILE_TYPE = 'D' and SYSTEM_TABLE_SCHEMA = :Libreria1 group by DESCRIPTION with rollup), LIB2 as (select DESCRIPTION, count(*) as NUM from SYSTABLES inner join (values ('A', 'ALIAS'), ('L', 'LOGICAL FILE'), ('M', 'MQT'), ('P', 'TABLE'), ('T', 'TABLE'), ('V', 'VIEW')) as TYPE(CODE, DESCRIPTION) on TABLE_TYPE = TYPE.CODE where FILE_TYPE = 'D' and SYSTEM_TABLE_SCHEMA = :Libreria2 group by DESCRIPTION with rollup) select coalesce(L1.DESCRIPTION, L2.DESCRIPTION) as "Tipo oggetto", coalesce(L1.NUM, 0) "Conteggio per lib 1", coalesce(L2.NUM, 0) "Conteggio per lib 2" from LIB1 as L1 full outer join LIB2 as L2 on coalesce(L1.DESCRIPTION, 'TOTALE') = coalesce(L2.DESCRIPTION, 'TOTALE') order by 1;
Confronto dettagliato della lista oggetti di database delle due librerie:
-- [F15] confronto lista oggetti database mancanti tra due librerie with LIB1 as (select SYSTEM_TABLE_NAME, TABLE_NAME, TABLE_TYPE, TABLE_TEXT, LAST_ALTERED_TIMESTAMP from SYSTABLES where FILE_TYPE = 'D' and SYSTEM_TABLE_SCHEMA = :Libreria1 ), LIB2 as (select SYSTEM_TABLE_NAME, TABLE_NAME, TABLE_TYPE, TABLE_TEXT, LAST_ALTERED_TIMESTAMP from SYSTABLES where FILE_TYPE = 'D' and SYSTEM_TABLE_SCHEMA = :Libreria2 ) select coalesce(LIB1.SYSTEM_TABLE_NAME, LIB2.SYSTEM_TABLE_NAME), coalesce(LIB1.TABLE_NAME, LIB2.TABLE_NAME) from LIB1 -- oggetti mancanti in Libreria1 rispetto a Libreria2 --right join LIB2 -- oggetti mancanti in Libreria2 rispetto a Libreria1 exception join LIB2 on LIB1.SYSTEM_TABLE_NAME = LIB2.SYSTEM_TABLE_NAME -- oggetti mancanti in Libreria1 rispetto a Libreria2 --where LIB1.SYSTEM_TABLE_NAME is null order by 1;
Confrontare due stream files
Stream file di esempio: aeroporti_certificati.csv
in cartella /home/MR73/FLR1
e /home/MR73/FLR2
Metodo 1: funzione hash*
Anche per gli stream file è possibile adottare una logica di confronto basato sul calcolo dell’hash. Così come per le tabelle abbiamo utilizzato la funzione hash_row
per calcolare l’hash del record, in modo simile possiamo utilizzare una delle funzioni hash (HASH_MD5, HASH_SHA1, HASH_SHA256, and HASH_SHA512) per calcolare l’hash dell’intero stream file. Lo stream file viene letto da IFS tramite la funzione GET_CLOB_FROM_FILE e passato come parametro alla funzione hash*.
select hash_md5(get_clob_from_file('/home/MR73/FLR1/aeroporti_certificati.csv')) as F1, hash_md5(get_clob_from_file('/home/MR73/FLR2/aeroporti_certificati.csv')) as F2 from SYSIBM/SYSDUMMY1;
Comparazione membri sorgente
La funzione GET_CLOB_FROM_FILE
consente di leggere non solo stream file ma anche membri di file sorgenti, quindi è possibile calcolare l’hash di due membri sorgente per effettuare un confronto rapido.
select hash_md5(get_clob_from_file('MR731/QTOOLS(GENCMDXML)')) as M1, hash_md5(get_clob_from_file('MR732/QTOOLS(GENCMDXML)')) as M2 from SYSIBM/SYSDUMMY1;
Calcolo hash con md5sum
L’hash di uno stream file si può calcolare anche tramite il comando PASE md5sum
.
Per esempio:
Metodo 2: funzione COMPARE_IFS
Dalla versione IBM i 7.5 TR4 e 7.4 TR10 è disponibile la funzione COMPARE_IFS che consente di identificare gli attributi differenti tra due stream files.
Questa funzione consente di confrontare due stream files specifici oppure il contenuto di due cartelle di IFS. La funzione può lavorare solo sugli oggetti contenuti nel file system root (/) e QOpenSys.
Molto interessante il parametro RDB2
che consente di specificare per il secondo file il nome del db remoto.
Il parametro COMPARE_ATTRIBUTES
consente di scegliere a quale livello di dettaglio di desidera conoscere le differenze:
'NAMES'
: viene restituito la lista dei nomi file non presenti in entrambe le cartelle. Valido solo se gli argomenti 1 e 2 della funzione sono due percorsi e non due stream files specifici'YES'
: viene restituito il dettaglio delle differenze tra i vari attributi
Per esempio:
select * from table(COMPARE_IFS(START_PATH_NAME1 => '/home/MR73/FLR1/aeroporti_certificati.csv', START_PATH_NAME2 => '/home/MR73/FLR2/aeroporti_certificati.csv', COMPARE_ATTRIBUTES => 'YES' ) );
Metodo 3: comandi PASE
Un metodo alternativo per confrontare due stream file si basa sull’uso dei comandi PASE bdiff, diff, sdiff o cmp.
bdiff
e diff
sono simili: bdiff
viene usato per confrontare file di grandi dimensioni
sdiff
esegue un confronto side-by-side
cmp
restituisce un’informazione sintetica sul confronto dei due file
Vediamo qualche esempio
diff -uw /home/MR73/FLR1/aeroporti_certificati.csv /home/MR73/FLR2/aeroporti_certificati.csv --- /home/MR73/FLR1/aeroporti_ce9rtificati.csv 2025-08-10 22:57:40.000000000 +0000 +++ /home/MR73/FLR2/aeroporti_certificati.csv 2025-08-10 23:07:14.000000000 +0000 @@ -1,4 +1,7 @@ BGY BERGAMO ORIO AL SERIO Via Aeroporto, 13, 24050 Orio al Serio (BG) ORIO AL SERIO 45.668889 9.700278 -VBS BRESCIA MONTICHIARI Via Aeroporto, 34, 25018 Montichiari (BS) MONTICHIARI 45.425556 10.326944 -LIN MILANO LINATE Viale Enrico Forlanini, 20054 Segrate (MI) SEGRATE 45.449444 9.278333 -MXP MILANO MALPENSA Aeroporto Milano Malpensa - 21010 Ferno (VA) FERNO 45.63 8.723056 +CIA ROMA CIAMPINO Via Appia Nuova, 1651, 00040 Ciampino (RM) CIAMPINO 41.799444 12.597222 +EBA MARINA DI CAMPO Via dell'Aeroporto LocalitÇÿ La Pila, 208, 57034 Marina di Campo (LI) CAMPO NELL'ELBA 42.762778 10.240556 +FCO ROMA FIUMICINO Via dell'Aeroporto di Fiumicino 320 - 00054 Fiumicino (RM) FIUMICINO 41.800278 12.238889 +LIN MILANO LINATE Viale Enrico Forlanini, 20090 (MI) PESCHIERA BORROMEO 45.449444 9.278333 +LMP LAMPEDUSA Contrada Cala Francese, 92010 Lampedusa E Linosa (AG) LAMPEDUSA 35.495833 12.611111 +MXP MILANO MALPENSA Aeroporto Berlusconi - 21010 Ferno (VA) FERNO 45.63 8.723056
diff -C 0 -tw /home/MR73/FLR1/aeroporti_certificati.csv /home/MR73/FLR2/aeroporti_certificati.csv *** /home/MR73/FLR1/aeroporti_certificati.csv Sun Aug 10 22:57:40 2025 --- /home/MR73/FLR2/aeroporti_certificati.csv Sun Aug 10 23:07:14 2025 *************** *** 2,4 **** ! VBS BRESCIA MONTICHIARI Via Aeroporto, 34, 25018 Montichiari (BS) MONTICHIARI 45.425556 10.326944 ! LIN MILANO LINATE Viale Enrico Forlanini, 20054 Segrate (MI) SEGRATE 45.449444 9.278333 ! MXP MILANO MALPENSA Aeroporto Milano Malpensa - 21010 Ferno (VA) FERNO 45.63 8.723056 --- 2,7 ---- ! CIA ROMA CIAMPINO Via Appia Nuova, 1651, 00040 Ciampino (RM) CIAMPINO 41.799444 12.597222 ! EBA MARINA DI CAMPO Via dell'Aeroporto LocalitÇÿ La Pila, 208, 57034 Marina di Campo (LI) CAMPO NELL'ELBA 42.762778 10.240556 ! FCO ROMA FIUMICINO Via dell'Aeroporto di Fiumicino 320 - 00054 Fiumicino (RM) FIUMICINO 41.800278 12.238889 ! LIN MILANO LINATE Viale Enrico Forlanini, 20090 (MI) PESCHIERA BORROMEO 45.449444 9.278333 ! LMP LAMPEDUSA Contrada Cala Francese, 92010 Lampedusa E Linosa (AG) LAMPEDUSA 35.495833 12.611111 ! MXP MILANO MALPENSA Aeroporto Berlusconi - 21010 Ferno (VA) FERNO 45.63 8.723056
diff -U 0 -tw /home/MR73/FLR1/aeroporti_certificati.csv /home/MR73/FLR2/aeroporti_certificati.csv --- /home/MR73/FLR1/aeroporti_certificati.csv 2025-08-10 22:57:40.000000000 +0000 +++ /home/MR73/FLR2/aeroporti_certificati.csv 2025-08-10 23:07:14.000000000 +0000 @@ -2,3 +2,6 @@ -VBS BRESCIA MONTICHIARI Via Aeroporto, 34, 25018 Montichiari (BS) MONTICHIARI 45.425556 10.326944 -LIN MILANO LINATE Viale Enrico Forlanini, 20054 Segrate (MI) SEGRATE 45.449444 9.278333 -MXP MILANO MALPENSA Aeroporto Milano Malpensa - 21010 Ferno (VA) FERNO 45.63 8.723056 +CIA ROMA CIAMPINO Via Appia Nuova, 1651, 00040 Ciampino (RM) CIAMPINO 41.799444 12.597222 +EBA MARINA DI CAMPO Via dell'Aeroporto LocalitÇÿ La Pila, 208, 57034 Marina di Campo (LI) CAMPO NELL'ELBA 42.762778 10.240556 +FCO ROMA FIUMICINO Via dell'Aeroporto di Fiumicino 320 - 00054 Fiumicino (RM) FIUMICINO 41.800278 12.238889 +LIN MILANO LINATE Viale Enrico Forlanini, 20090 (MI) PESCHIERA BORROMEO 45.449444 9.278333 +LMP LAMPEDUSA Contrada Cala Francese, 92010 Lampedusa E Linosa (AG) LAMPEDUSA 35.495833 12.611111 +MXP MILANO MALPENSA Aeroporto Berlusconi - 21010 Ferno (VA) FERNO 45.63 8.723056
sdiff /home/MR73/FLR1/aeroporti_certificati.csv /home/MR73/FLR2/aeroporti_certificati.csv BGY BERGAMO ORIO AL SERIO Via Aeroporto, 13, 24050 Orio BGY BERGAMO ORIO AL SERIO Via Aeroporto, 13, 24050 Orio VBS BRESCIA MONTICHIARI Via Aeroporto, 34, 25018 Monti | CIA ROMA CIAMPINO Via Appia Nuova, 1651, 00040 Ciampino LIN MILANO LINATE Viale Enrico Forlanini, 20054 Segrate | EBA MARINA DI CAMPO Via dell'Aeroporto LocalitÇÿ La Pila MXP MILANO MALPENSA Aeroporto Milano Malpensa - 21010 Fern | FCO ROMA FIUMICINO Via dell'Aeroporto di Fiumicino 320 - > LIN MILANO LINATE Viale Enrico Forlanini, 20090 (MI) > LMP LAMPEDUSA Contrada Cala Francese, 92010 Lampedus > MXP MILANO MALPENSA Aeroporto Berlusconi - 21010 Ferno (VA
cmp /home/MR73/FLR1/aeroporti_certificati.csv /home/MR73/FLR2/aeroporti_certificati.csv /home/MR73/FLR1/aeroporti_certificati.csv /home/MR73/FLR2/aeroporti_certificati.csv differ: char 107, line 2 N.B. se non ci sono differenze nei due file il comando cmp non restituisce nulla in output
Per l’utilizzo del comando cmp
integrato in una funzione RPG suggerisco la lettura dell’articolo (con allegato esempio RPG) TechTip: Using Qshell from RPG to Compare Two Files di Aaron Bartell.
Confrontare due cartelle di IFS
Metodo 1: funzione IFS_OBJECT_STATISTICS
Utilizzando la funzione IFS_OBJECT_STATISTICS (gemella di OBJECT_STATISTICS) è possibile costruire una query di confronto del contenuto di due cartelle di IFS.
Per esempio con la seguente query è possibile ottenere l’elenco del contenuto della cartella /home/MR73/FLR1 e /home/MR73/FLR2 e trovare eventuali oggetti mancanti in una cartella rispetto all’altra ed anche gli oggetti presenti in entrambe ma con alcune proprietà non congruenti (nell’esempio si confronta data/ora di creazione, data/ora di modifica, dimensione e CCSID)
select SYSTOOLS.IFS_PATH(PATH_NAME => F1.PATH_NAME , SUBSECTION => 'FILE NAME') as "F1 file", F1.CREATE_TIMESTAMP as "Data/ora creaz.", F1.DATA_CHANGE_TIMESTAMP as "Data/ora mod.", F1.DATA_SIZE as "Dim.", F1.CCSID as "CCSID", SYSTOOLS.IFS_PATH(PATH_NAME => F2.PATH_NAME , SUBSECTION => 'FILE NAME') as "F2 file", F2.CREATE_TIMESTAMP as "Data/ora creaz.", F2.DATA_CHANGE_TIMESTAMP as "Data/ora mod.", F2.DATA_SIZE as "Dim.", F2.CCSID as "CCSID" from table(IFS_OBJECT_STATISTICS(START_PATH_NAME => '/home/MR73/FLR1', OBJECT_TYPE_LIST => '*ALLSTMF', IGNORE_ERRORS => 'YES')) as F1 full outer join table(IFS_OBJECT_STATISTICS(START_PATH_NAME => '/home/MR73/FLR2', OBJECT_TYPE_LIST => '*ALLSTMF', IGNORE_ERRORS => 'YES')) as F2 on SYSTOOLS.IFS_PATH(PATH_NAME => F1.PATH_NAME , SUBSECTION => 'FILE NAME') = SYSTOOLS.IFS_PATH(PATH_NAME => F2.PATH_NAME , SUBSECTION => 'FILE NAME') -- specificare gli attributi da confrontare where F1.CREATE_TIMESTAMP is distinct from F2.CREATE_TIMESTAMP -- data/ora creazioe or F1.DATA_CHANGE_TIMESTAMP is distinct from F2.DATA_CHANGE_TIMESTAMP -- data/ora modifica or F1.DATA_SIZE is distinct from F2.DATA_SIZE -- dimensione or F1.CCSID is distinct from F2.CCSID -- CCSID order by case when F1.PATH_NAME is not null then lower(SYSTOOLS.IFS_PATH(PATH_NAME => F1.PATH_NAME , SUBSECTION => 'FILE NAME')) else lower(SYSTOOLS.IFS_PATH(PATH_NAME => F2.PATH_NAME , SUBSECTION => 'FILE NAME')) end;
Metodo 2: funzione COMPARE_IFS
Sempre con la funzione SQL COMPARE_IFS
è possibile confrontare non solo due stream file ma due intere cartelle ed eventualmente anche le sottocartelle (parametro SUBTREE_DIRECTORIES
).
Confronto specificando il parametro COMPARE_ATTRIBUTES uguale a ‘NAMES’
select * from table(COMPARE_IFS(START_PATH_NAME1 => '/home/MR73/FLR1', START_PATH_NAME2 => '/home/MR73/FLR2', COMPARE_ATTRIBUTES => 'NAMES' ) );
Per un confronto più dettagliato si può specificare il parametro COMPARE_ATTRIBUTES uguale a ‘YES’
Metodo 3: comandi PASE
Il medesimo comando diff utilizzato per confrontare due stream file può anche essere utilizzato per confrontare il contenuto di tutti i file contenuti in due cartelle di IFS.
Per i file presenti in entrambe le cartelle viene eseguito un comando diff per confrontare il contenuto dei due file. Per i file presenti solo in una delle due cartelle viene evidenziato “Only in…” in quale cartella si trova.
diff /home/MR73/FLR1 /home/MR73/FLR2 Only in /home/MR73/FLR1: aeroporti_certificati.bak diff /home/MR73/FLR1/aeroporti_certificati.csv /home/MR73/FLR2/aeroporti_certificati.csv 2,4c2,7 < VBS BRESCIA MONTICHIARI Via Aeroporto, 34, 25018 Montichiari (BS) MONTICHIARI 45.425556 10.326944 < LIN MILANO LINATE Viale Enrico Forlanini, 20054 Segrate (MI) SEGRATE 45.449444 9.278333 < MXP MILANO MALPENSA Aeroporto Milano Malpensa - 21010 Ferno (VA) FERNO 45.63 8.723056 --- > CIA ROMA CIAMPINO Via Appia Nuova, 1651, 00040 Ciampino (RM) CIAMPINO 41.799444 12.597222 > EBA MARINA DI CAMPO Via dell'Aeroporto LocalitÇÿ La Pila, 208, 57034 Marina di Campo (LI) CAMPO NELL'ELBA 42.762778 10.240556 > FCO ROMA FIUMICINO Via dell'Aeroporto di Fiumicino 320 - 00054 Fiumicino (RM) FIUMICINO 41.800278 12.238889 > LIN MILANO LINATE Viale Enrico Forlanini, 20090 (MI) PESCHIERA BORROMEO 45.449444 9.278333 > LMP LAMPEDUSA Contrada Cala Francese, 92010 Lampedusa E Linosa (AG) LAMPEDUSA 35.495833 12.611111 > MXP MILANO MALPENSA Aeroporto Berlusconi - 21010 Ferno (VA) FERNO 45.63 8.723056 Only in /home/MR73/FLR2: aeroporti_certificati_v2.bak Only in /home/MR73/FLR1: esempio.txt Only in /home/MR73/FLR2: response.json
Per confrontare le due cartelle è possibile utilizzare anche il comando dircmp. E’ equivalente al comando diff -r
.
Per poter eseguire il comando dircmp
occorre accedere in scrittura alla cartella /usr/tmp
.