1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-29 21:50:05 +00:00

Compare commits

...

9 Commits

Author SHA1 Message Date
Allan Bowe
a9185a2bf2 Merge pull request #338 from sasjs/issue337
fix: adding support for multilabel and notsorted formats
2023-06-21 18:24:16 +01:00
Allan
f0b77dfc6a chore: removing mp_ds2md dump from mp_loadformat.test.2.sas 2023-06-21 17:02:12 +01:00
Allan
91c4b87496 chore(docs): updating markdown table in mp_loadformat.test.2.sas 2023-06-21 17:00:49 +01:00
Allan
111d0dffc3 chore: removing redundant dependency from header 2023-06-21 16:46:31 +01:00
Allan
4f481ec8b4 fix: adding support for multilabel and notsorted formats
included additional test job covering multiple scenarios.  Closes #337
2023-06-21 16:41:46 +01:00
Allan
b8cec22a88 fix: mp_aligndecimal in wrong dependency section 2023-06-20 17:40:56 +01:00
Allan
6b1accdd6b chore(docs): updating comments 2023-06-20 17:18:53 +01:00
Allan Bowe
949b406c23 Update FUNDING.yml 2023-06-20 11:48:00 +01:00
Allan Bowe
fc90a7f928 Update FUNDING.yml
chore: removing github sponsorship option and replacing with nostr

https://iris.to/npub1sasjs00efhywf9uu754wxcetd32edenrczl274ks7ju2y2yn70sqvzsdhn
2023-06-20 11:36:59 +01:00
10 changed files with 338 additions and 154 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,3 @@
# These are supported funding model platforms
github: [sasjs]
custom: https://getalby.com/p/sasjs

135
all.sas
View File

@@ -2696,6 +2696,7 @@ and %superq(SYSPROCESSNAME) ne %str(Compute Server)
aligndp4e49996=right(aligndp4e49996);
myvar=aligndp4e49996;
end;
drop aligndp4e49996;
Results (myvar variable):
@@ -2721,7 +2722,7 @@ and %superq(SYSPROCESSNAME) ne %str(Compute Server)
7998580.8415
@param var The (data step) variable to create
@param var The (data step, character) variable to modify
@param width= (8) The number of characters BEFORE the decimal point
<h4> SAS Macros </h4>
@@ -4118,10 +4119,10 @@ run;
<h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_getuniquename.sas
@li mp_aligndecimal.sas
<h4> Related Macros </h4>
@li mf_getvarformat.sas
@li mp_aligndecimal.sas
@li mp_getformats.sas
@li mp_loadformat.sas
@li mp_ds2fmtds.sas
@@ -4160,16 +4161,24 @@ proc format lib=&libcat cntlout=&cntlds;
%end;
run;
data &cntlout;
data &cntlout/nonote2err;
if 0 then set &ddlds;
set &cntlds;
if type in ("I","N") then do; /* numeric (in)format */
by type fmtname notsorted;
/* align the numeric values to avoid overlapping ranges */
if type in ("I","N") then do;
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
end;
/* create row marker. Data cannot be sorted without it! */
if first.fmtname then fmtrow=0;
fmtrow+1;
run;
proc sort;
by type fmtname start;
by type fmtname fmtrow;
run;
proc sql;
@@ -6013,6 +6022,7 @@ run;
options ps=max lrecl=max;
data _null_;
infile &outref;
if _n_=1 then putlog "# &libds" /;
input;
putlog _infile_;
run;
@@ -10118,10 +10128,10 @@ select distinct lowcase(memname)
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
<h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_getuniquename.sas
@li mf_nobs.sas
@li mp_abort.sas
@li mp_aligndecimal.sas
@li mp_cntlout.sas
@li mp_lockanytable.sas
@li mp_storediffs.sas
@@ -10129,8 +10139,8 @@ select distinct lowcase(memname)
<h4> Related Macros </h4>
@li mddl_dc_difftable.sas
@li mddl_dc_locktable.sas
@li mp_aligndecimal.sas
@li mp_loadformat.test.sas
@li mp_loadformat.test.1.sas
@li mp_loadformat.test.2.sas
@li mp_lockanytable.sas
@li mp_stackdiffs.sas
@@ -10163,13 +10173,6 @@ select distinct lowcase(memname)
%let &var=%upcase(&prefix._&var);
%end;
/*
format values can be up to 32767 wide. SQL joins on such a wide column can
cause buffer issues. Update ibufsize and reset at the end.
*/
%let ibufsize=%sysfunc(getoption(ibufsize));
options ibufsize=32767 ;
/* in DC, format catalogs maybe specified in the libds with a -FC extension */
%let libcat=%scan(&libcat,1,-);
@@ -10232,16 +10235,24 @@ select distinct
%mp_cntlout(libcat=&libcat,fmtlist=&fmtlist,cntlout=&base_fmts)
/* get a hash of the row */
%local cvars nvars;
%let cvars=TYPE FMTNAME START END LABEL PREFIX FILL SEXCL EEXCL HLO DECSEP
DIG3SEP DATATYPE LANGUAGE;
%let nvars=FMTROW MIN MAX DEFAULT LENGTH FUZZ MULT NOEDIT;
data &base_fmts/note2err;
set &base_fmts;
fmthash=%mp_md5(cvars=&cvars, nvars=&nvars);
run;
/**
* Ensure input table and base_formats have consistent lengths and types
*/
%mddl_sas_cntlout(libds=&template)
data &inlibds;
length &delete_col $3;
if 0 then set &template;
length start end $10000;
data &inlibds/nonote2err;
length &delete_col $3 FMTROW 8 start end label $32767;
if 0 then set &base_fmts;
set &libds;
by type fmtname notsorted;
if &delete_col='' then &delete_col='No';
fmtname=upcase(fmtname);
type=upcase(type);
@@ -10259,6 +10270,14 @@ data &inlibds;
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
end;
/* update row marker - retain new var as fmtrow may already be in libds */
if first.fmtname then row=1;
else row+1;
drop row;
fmtrow=row;
fmthash=%mp_md5(cvars=&cvars, nvars=&nvars);
run;
/**
@@ -10269,12 +10288,10 @@ create table &outds_add(drop=&delete_col) as
select a.*
from &inlibds a
left join &base_fmts b
on a.fmtname=b.fmtname
and a.start=b.start
and a.type=b.type
on a.type=b.type and a.fmtname=b.fmtname and a.fmtrow=b.fmtrow
where b.fmtname is null
and upcase(a.&delete_col) ne "YES"
order by type, fmtname, start;
order by type, fmtname, fmtrow;
/**
* Identify deleted records
@@ -10283,11 +10300,9 @@ create table &outds_del(drop=&delete_col) as
select a.*
from &inlibds a
inner join &base_fmts b
on a.fmtname=b.fmtname
and a.start=b.start
and a.type=b.type
on a.type=b.type and a.fmtname=b.fmtname and a.fmtrow=b.fmtrow
where upcase(a.&delete_col)="YES"
order by type, fmtname, start;
order by type, fmtname, fmtrow;
/**
* Identify modified records
@@ -10296,13 +10311,10 @@ create table &outds_mod (drop=&delete_col) as
select a.*
from &inlibds a
inner join &base_fmts b
on a.fmtname=b.fmtname
and a.start=b.start
and a.type=b.type
on a.type=b.type and a.fmtname=b.fmtname and a.fmtrow=b.fmtrow
where upcase(a.&delete_col) ne "YES"
order by type, fmtname, start;
options ibufsize=&ibufsize;
and a.fmthash ne b.fmthash
order by type, fmtname, fmtrow;
%mp_abort(
iftrue=(&syscc ne 0)
@@ -10311,19 +10323,21 @@ options ibufsize=&ibufsize;
)
%if &loadtarget=YES %then %do;
/* new records plus base records that are not deleted or modified */
data &ds1;
merge &base_fmts(in=base)
&outds_mod(in=mod)
&outds_add(in=add)
&outds_del(in=del);
if not del and not mod;
by type fmtname start;
by type fmtname fmtrow;
run;
/* add back the modified records */
data &stagedata;
set &ds1 &outds_mod;
run;
proc sort;
by type fmtname start;
by type fmtname fmtrow;
run;
%end;
/* mp abort needs to run outside of conditional blocks */
@@ -10371,7 +10385,7 @@ options ibufsize=&ibufsize;
%mp_storediffs(&libcat-FC
,&base_fmts
,TYPE FMTNAME START
,TYPE FMTNAME FMTROW
,delds=&outds_del
,modds=&outds_mod
,appds=&outds_add
@@ -12795,9 +12809,9 @@ run;
%if %index(&libds,-)>0 and %scan(&libds,2,-)=FC %then %do;
/* this is a format catalog - cannot query cols directly */
%let vlist="FMTNAME","START","END","LABEL","MIN","MAX","DEFAULT","LENGTH"
,"FUZZ","PREFIX","MULT","FILL","NOEDIT","TYPE","SEXCL","EEXCL","HLO"
,"DECSEP","DIG3SEP","DATATYPE","LANGUAGE";
%let vlist="TYPE","FMTNAME","FMTROW","START","END","LABEL","MIN","MAX"
,"DEFAULT","LENGTH","FUZZ","PREFIX","MULT","FILL","NOEDIT","SEXCL"
,"EEXCL","HLO","DECSEP","DIG3SEP","DATATYPE","LANGUAGE";
%end;
%else %let vlist=%mf_getvarlist(&libds,dlm=%str(,),quote=DOUBLE);
@@ -14065,31 +14079,28 @@ ods package close;
%mend mddl_dc_maxkeytable;/**
@file
@brief The CNTLOUT table generated by proc format
@details This table will actually change format depending on the data values,
therefore the max possible lengths are described here to enable consistency
when dealing with format data.
@details The actual CNTLOUT table may have varying variable lengths,
depending on the data values, therefore the max possible lengths
(given various practical restrictions) are described here to enable
consistency when dealing with format data.
**/
%macro mddl_sas_cntlout(libds=WORK.CNTLOUT);
proc sql;
create table &libds(
TYPE char(1) label='Type of format'
,FMTNAME char(32) label='Format name'
/*
to accommodate larger START values, mp_loadformat.sas will need the
SQL dependency removed (proc sql needs to accommodate 3 index values in
a 32767 ibufsize limit)
*/
,START char(10000) label='Starting value for format'
proc sql;
create table &libds(
TYPE char(1) label='Type of format - either N (num fmt), C (char fmt), I (num infmt) or J (char infmt)'
,FMTNAME char(32) label='Format name'
,FMTROW num label='CALCULATED Position of record by FMTNAME (reqd for multilabel formats)'
,START char(32767) label='Starting value for format'
/*
Keep lengths of START and END the same to avoid this err:
"Start is greater than end: -<."
Similar usage note: https://support.sas.com/kb/69/330.html
*/
,END char(10000) label='Ending value for format'
,END char(32767) label='Ending value for format'
,LABEL char(32767) label='Format value label'
,MIN num length=3 label='Minimum length'
,MAX num length=3 label='Maximum length'
@@ -14102,12 +14113,24 @@ create table &libds(
,NOEDIT num length=3 label='Is picture string noedit?'
,SEXCL char(1) label='Start exclusion'
,EEXCL char(1) label='End exclusion'
,HLO char(13) label='Additional information'
,HLO char(13) label='Additional information. M=MultiLabel'
,DECSEP char(1) label='Decimal separator'
,DIG3SEP char(1) label='Three-digit separator'
,DATATYPE char(8) label='Date/time/datetime?'
,LANGUAGE char(8) label='Language for date strings'
);
);
%local lib;
%let libds=%upcase(&libds);
%if %index(&libds,.)=0 %then %let lib=WORK;
%else %let lib=%scan(&libds,1,.);
proc datasets lib=&lib noprint;
modify %scan(&libds,-1,.);
index create
pk_cntlout=(type fmtname fmtrow)
/nomiss unique;
quit;
%mend mddl_sas_cntlout;
/**

View File

@@ -36,6 +36,7 @@
aligndp4e49996=right(aligndp4e49996);
myvar=aligndp4e49996;
end;
drop aligndp4e49996;
Results (myvar variable):
@@ -61,7 +62,7 @@
7998580.8415
@param var The (data step) variable to create
@param var The (data step, character) variable to modify
@param width= (8) The number of characters BEFORE the decimal point
<h4> SAS Macros </h4>

View File

@@ -25,10 +25,10 @@
<h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_getuniquename.sas
@li mp_aligndecimal.sas
<h4> Related Macros </h4>
@li mf_getvarformat.sas
@li mp_aligndecimal.sas
@li mp_getformats.sas
@li mp_loadformat.sas
@li mp_ds2fmtds.sas
@@ -67,16 +67,24 @@ proc format lib=&libcat cntlout=&cntlds;
%end;
run;
data &cntlout;
data &cntlout/nonote2err;
if 0 then set &ddlds;
set &cntlds;
if type in ("I","N") then do; /* numeric (in)format */
by type fmtname notsorted;
/* align the numeric values to avoid overlapping ranges */
if type in ("I","N") then do;
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
end;
/* create row marker. Data cannot be sorted without it! */
if first.fmtname then fmtrow=0;
fmtrow+1;
run;
proc sort;
by type fmtname start;
by type fmtname fmtrow;
run;
proc sql;

View File

@@ -95,6 +95,7 @@ run;
options ps=max lrecl=max;
data _null_;
infile &outref;
if _n_=1 then putlog "# &libds" /;
input;
putlog _infile_;
run;

View File

@@ -34,10 +34,10 @@
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
<h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_getuniquename.sas
@li mf_nobs.sas
@li mp_abort.sas
@li mp_aligndecimal.sas
@li mp_cntlout.sas
@li mp_lockanytable.sas
@li mp_storediffs.sas
@@ -45,8 +45,8 @@
<h4> Related Macros </h4>
@li mddl_dc_difftable.sas
@li mddl_dc_locktable.sas
@li mp_aligndecimal.sas
@li mp_loadformat.test.sas
@li mp_loadformat.test.1.sas
@li mp_loadformat.test.2.sas
@li mp_lockanytable.sas
@li mp_stackdiffs.sas
@@ -79,13 +79,6 @@
%let &var=%upcase(&prefix._&var);
%end;
/*
format values can be up to 32767 wide. SQL joins on such a wide column can
cause buffer issues. Update ibufsize and reset at the end.
*/
%let ibufsize=%sysfunc(getoption(ibufsize));
options ibufsize=32767 ;
/* in DC, format catalogs maybe specified in the libds with a -FC extension */
%let libcat=%scan(&libcat,1,-);
@@ -148,16 +141,24 @@ select distinct
%mp_cntlout(libcat=&libcat,fmtlist=&fmtlist,cntlout=&base_fmts)
/* get a hash of the row */
%local cvars nvars;
%let cvars=TYPE FMTNAME START END LABEL PREFIX FILL SEXCL EEXCL HLO DECSEP
DIG3SEP DATATYPE LANGUAGE;
%let nvars=FMTROW MIN MAX DEFAULT LENGTH FUZZ MULT NOEDIT;
data &base_fmts/note2err;
set &base_fmts;
fmthash=%mp_md5(cvars=&cvars, nvars=&nvars);
run;
/**
* Ensure input table and base_formats have consistent lengths and types
*/
%mddl_sas_cntlout(libds=&template)
data &inlibds;
length &delete_col $3;
if 0 then set &template;
length start end $10000;
data &inlibds/nonote2err;
length &delete_col $3 FMTROW 8 start end label $32767;
if 0 then set &base_fmts;
set &libds;
by type fmtname notsorted;
if &delete_col='' then &delete_col='No';
fmtname=upcase(fmtname);
type=upcase(type);
@@ -175,6 +176,14 @@ data &inlibds;
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
end;
/* update row marker - retain new var as fmtrow may already be in libds */
if first.fmtname then row=1;
else row+1;
drop row;
fmtrow=row;
fmthash=%mp_md5(cvars=&cvars, nvars=&nvars);
run;
/**
@@ -185,12 +194,10 @@ create table &outds_add(drop=&delete_col) as
select a.*
from &inlibds a
left join &base_fmts b
on a.fmtname=b.fmtname
and a.start=b.start
and a.type=b.type
on a.type=b.type and a.fmtname=b.fmtname and a.fmtrow=b.fmtrow
where b.fmtname is null
and upcase(a.&delete_col) ne "YES"
order by type, fmtname, start;
order by type, fmtname, fmtrow;
/**
* Identify deleted records
@@ -199,11 +206,9 @@ create table &outds_del(drop=&delete_col) as
select a.*
from &inlibds a
inner join &base_fmts b
on a.fmtname=b.fmtname
and a.start=b.start
and a.type=b.type
on a.type=b.type and a.fmtname=b.fmtname and a.fmtrow=b.fmtrow
where upcase(a.&delete_col)="YES"
order by type, fmtname, start;
order by type, fmtname, fmtrow;
/**
* Identify modified records
@@ -212,13 +217,10 @@ create table &outds_mod (drop=&delete_col) as
select a.*
from &inlibds a
inner join &base_fmts b
on a.fmtname=b.fmtname
and a.start=b.start
and a.type=b.type
on a.type=b.type and a.fmtname=b.fmtname and a.fmtrow=b.fmtrow
where upcase(a.&delete_col) ne "YES"
order by type, fmtname, start;
options ibufsize=&ibufsize;
and a.fmthash ne b.fmthash
order by type, fmtname, fmtrow;
%mp_abort(
iftrue=(&syscc ne 0)
@@ -227,19 +229,21 @@ options ibufsize=&ibufsize;
)
%if &loadtarget=YES %then %do;
/* new records plus base records that are not deleted or modified */
data &ds1;
merge &base_fmts(in=base)
&outds_mod(in=mod)
&outds_add(in=add)
&outds_del(in=del);
if not del and not mod;
by type fmtname start;
by type fmtname fmtrow;
run;
/* add back the modified records */
data &stagedata;
set &ds1 &outds_mod;
run;
proc sort;
by type fmtname start;
by type fmtname fmtrow;
run;
%end;
/* mp abort needs to run outside of conditional blocks */
@@ -287,7 +291,7 @@ options ibufsize=&ibufsize;
%mp_storediffs(&libcat-FC
,&base_fmts
,TYPE FMTNAME START
,TYPE FMTNAME FMTROW
,delds=&outds_del
,modds=&outds_mod
,appds=&outds_add

View File

@@ -147,9 +147,9 @@ run;
%if %index(&libds,-)>0 and %scan(&libds,2,-)=FC %then %do;
/* this is a format catalog - cannot query cols directly */
%let vlist="FMTNAME","START","END","LABEL","MIN","MAX","DEFAULT","LENGTH"
,"FUZZ","PREFIX","MULT","FILL","NOEDIT","TYPE","SEXCL","EEXCL","HLO"
,"DECSEP","DIG3SEP","DATATYPE","LANGUAGE";
%let vlist="TYPE","FMTNAME","FMTROW","START","END","LABEL","MIN","MAX"
,"DEFAULT","LENGTH","FUZZ","PREFIX","MULT","FILL","NOEDIT","SEXCL"
,"EEXCL","HLO","DECSEP","DIG3SEP","DATATYPE","LANGUAGE";
%end;
%else %let vlist=%mf_getvarlist(&libds,dlm=%str(,),quote=DOUBLE);

View File

@@ -1,31 +1,28 @@
/**
@file
@brief The CNTLOUT table generated by proc format
@details This table will actually change format depending on the data values,
therefore the max possible lengths are described here to enable consistency
when dealing with format data.
@details The actual CNTLOUT table may have varying variable lengths,
depending on the data values, therefore the max possible lengths
(given various practical restrictions) are described here to enable
consistency when dealing with format data.
**/
%macro mddl_sas_cntlout(libds=WORK.CNTLOUT);
proc sql;
create table &libds(
TYPE char(1) label='Type of format'
,FMTNAME char(32) label='Format name'
/*
to accommodate larger START values, mp_loadformat.sas will need the
SQL dependency removed (proc sql needs to accommodate 3 index values in
a 32767 ibufsize limit)
*/
,START char(10000) label='Starting value for format'
proc sql;
create table &libds(
TYPE char(1) label='Type of format - either N (num fmt), C (char fmt), I (num infmt) or J (char infmt)'
,FMTNAME char(32) label='Format name'
,FMTROW num label='CALCULATED Position of record by FMTNAME (reqd for multilabel formats)'
,START char(32767) label='Starting value for format'
/*
Keep lengths of START and END the same to avoid this err:
"Start is greater than end: -<."
Similar usage note: https://support.sas.com/kb/69/330.html
*/
,END char(10000) label='Ending value for format'
,END char(32767) label='Ending value for format'
,LABEL char(32767) label='Format value label'
,MIN num length=3 label='Minimum length'
,MAX num length=3 label='Maximum length'
@@ -38,11 +35,23 @@ create table &libds(
,NOEDIT num length=3 label='Is picture string noedit?'
,SEXCL char(1) label='Start exclusion'
,EEXCL char(1) label='End exclusion'
,HLO char(13) label='Additional information'
,HLO char(13) label='Additional information. M=MultiLabel'
,DECSEP char(1) label='Decimal separator'
,DIG3SEP char(1) label='Three-digit separator'
,DATATYPE char(8) label='Date/time/datetime?'
,LANGUAGE char(8) label='Language for date strings'
);
);
%local lib;
%let libds=%upcase(&libds);
%if %index(&libds,.)=0 %then %let lib=WORK;
%else %let lib=%scan(&libds,1,.);
proc datasets lib=&lib noprint;
modify %scan(&libds,-1,.);
index create
pk_cntlout=(type fmtname fmtrow)
/nomiss unique;
quit;
%mend mddl_sas_cntlout;

View File

@@ -6,6 +6,7 @@
<h4> SAS Macros </h4>
@li mddl_dc_difftable.sas
@li mp_aligndecimal.sas
@li mp_cntlout.sas
@li mp_loadformat.sas
@li mp_assert.sas
@li mp_assertscope.sas
@@ -23,9 +24,9 @@ data work.loadfmts;
length fmtname $32 start end $10000;
eexcl='Y';
type='N';
do i=1 to 100;
fmtname=cats('SASJS_',i,'X');
do j=1 to 100;
do i=1 to 10;
fmtname=cats('SASJS_',put(i,z4.),'X');
do j=1 to 20;
start=cats(j);
end=cats(j+1);
%mp_aligndecimal(start,width=16)
@@ -38,21 +39,32 @@ run;
proc format cntlin=work.loadfmts library=perm.testcat;
run;
/*
use actual format data as test baseline, as proc format adds attributes eg
min/max etc
*/
%mp_cntlout(libcat=perm.testcat,cntlout=work.loadfmts2)
/* make some test data */
data work.stagedata;
set work.loadfmts;
type='N';
eexcl='Y';
if _n_<150 then deleteme='Yes';
else if _n_<250 then label='mod'!!cats(_n_);
else if _n_<350 then do;
set work.loadfmts2 end=lastobs;
by type fmtname;
if lastobs then do;
output;
fmtname='NEWFMT'!!cats(_n_,'x'); /* 1 new record */
start=cats(_n_);
end=cats(_n_+1);
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
label='newval'!!cats(_N_);
label='newval'!!cats(_N_,'X');
output;
stop;
end;
else stop;
else if last.fmtname then deleteme='Yes'; /* 9 deletions */
else if first.fmtname then label='modified '!!cats(_n_); /* 10 changes */
output;
run;
/* load the above */
@@ -71,22 +83,22 @@ run;
%mp_assertscope(COMPARE)
%mp_assert(
iftrue=(%mf_nobs(del_test1)=149),
iftrue=(%mf_nobs(del_test1)=9),
desc=Test 1 - delete obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(add_test1)=100),
iftrue=(%mf_nobs(add_test1)=1),
desc=Test 1 - add obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(mod_test1)=100),
iftrue=(%mf_nobs(mod_test1)=10),
desc=Test 1 - mod obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(perm.audit)=7329),
iftrue=(%mf_nobs(perm.audit)=440),
desc=Test 1 - audit table updated,
outds=work.test_results
)
@@ -101,7 +113,7 @@ run;
)
/* set up a mix of formats */
data work.loadfmts2;
data work.loadfmts3;
length fmtname $32 start end $10000;
eexcl='Y';
type='J';
@@ -150,45 +162,47 @@ data work.loadfmts2;
end;
drop i j;
run;
proc format cntlin=work.loadfmts2 library=perm.testcat2;
proc format cntlin=work.loadfmts3 library=perm.testcat3;
run;
%mp_cntlout(libcat=perm.testcat3,cntlout=work.loadfmts4)
/* make some test data */
data work.stagedata2;
set work.loadfmts2;
data work.stagedata3;
set work.loadfmts4;
where type in ('I','J');
eexcl='Y';
by type fmtname notsorted;
if type='I' then do;
i+1;
if i<3 then deleteme='Yes';
else if i<7 then label= cats(ranuni(0)*100);
else if i<12 then do;
/* new values */
if last.fmtname then do;
deleteme='Yes'; /* 3 deletions */
output;
end;
else if fmtrow le 3 then do; /* 9 changed values */
z=ranuni(0)*1000000;
start=cats(z);
end=cats(z+1);
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
label= cats(ranuni(0)*100);
output;
end;
if i<12 then output;
end;
else do;
j+1;
if j<3 then deleteme='Yes';
else if j<7 then label= cats(ranuni(0)*100);
else if j<12 then do;
start= cats("NEWVAL",start);
end=start;
label= "NEWVAL "||cats(ranuni(0)*100);
if last.fmtname then do;
output; /* 6 new records */
x=_n_;
x+1;start=cats("mod",x);end=start;label='newlabel1';output;
x+1;start=cats("mod",x);end=start;label='newlabel2';output;
end;
else if fmtrow le 3 then do; /* 9 more changed values */
start= cats("mod",_n_);
end=start;
label= "mod "||cats(ranuni(0)*100);
output;
end;
if j<12 then output;
end;
run;
%mp_loadformat(perm.testcat2
,work.stagedata2
%mp_loadformat(perm.testcat3
,work.stagedata3
,loadtarget=YES
,auditlibds=perm.audit
,locklibds=0
@@ -200,17 +214,18 @@ run;
)
%mp_assert(
iftrue=(%mf_nobs(del_test2)=4),
iftrue=(%mf_nobs(del_test2)=3),
desc=Test 2 - delete obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(mod_test2)=8),
iftrue=(%mf_nobs(mod_test2)=18),
desc=Test 2 - mod obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(add_test2)=10),
iftrue=(%mf_nobs(add_test2)=6),
desc=Test 2 - add obs,
outds=work.test_results
)

View File

@@ -0,0 +1,123 @@
/**
@file
@brief Testing mp_loadformat.sas macro for multilabel formats
@details Multilabel records can be complete duplicates!! Also, the order is
important.
The provided formats create a table as follows:
|TYPE:$1.|FMTNAME:$32.|START:$10000.|END:$10000.|LABEL:$32767.|MIN:best.|MAX:best.|DEFAULT:best.|LENGTH:best.|FUZZ:best.|PREFIX:$2.|MULT:best.|FILL:$1.|NOEDIT:best.|SEXCL:$1.|EEXCL:$1.|HLO:$13.|DECSEP:$1.|DIG3SEP:$1.|DATATYPE:$8.|LANGUAGE:$8.|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|`C `|`GENDERML `|` `|` `|`Total people `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`C `|`GENDERML `|`1 `|`1 `|`Male `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`C `|`GENDERML `|`1 `|`1 `|`Total people `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`C `|`GENDERML `|`2 `|`2 `|`Female `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`C `|`GENDERML `|`2 `|`2 `|`Female `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`C `|`GENDERML `|`2 `|`2 `|`Thormale `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`C `|`GENDERML `|`2 `|`2 `|`Total people `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`N `|`AGEMLA `|`1 `|`4 `|`Preschool `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLA `|`1 `|`18 `|`Children `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLA `|`19 `|`120 `|`Adults `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLB `|`1 `|`4 `|`Preschool `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLB `|`1 `|`18 `|`Children `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLB `|`19 `|`120 `|`Adults `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLC `|`1 `|`18 `|`Children `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLC `|`1 `|`4 `|`Preschool `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLC `|`19 `|`120 `|`Adults `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
<h4> SAS Macros </h4>
@li mf_nobs.sas
@li mp_cntlout.sas
@li mp_loadformat.sas
@li mp_assert.sas
@li mp_assertdsobs.sas
**/
/* prep format catalog */
libname perm (work);
/* create some multilable formats */
%let cat1=perm.test1;
proc format library=&cat1;
value $genderml (multilabel notsorted)
'1'='Male'
'2'='Female'
'2'='Female'
'2'='Farmale'
'1','2',' '='Total people';
value agemla (multilabel)
1-4='Preschool'
1-18='Children'
19-120='Adults';
value agemlb (multilabel)
19-120='Adults'
1-18='Children'
1-4='Preschool';
value agemlc (multilabel notsorted)
19-120='Adults'
1-18='Children'
1-4='Preschool';
run;
%mp_cntlout(libcat=&cat1,cntlout=work.cntlout1)
%mp_assertdsobs(work.cntlout1,
desc=Has 16 records,
test=EQUALS 16
)
data work.stagedata3;
set work.cntlout1;
if fmtname='AGEMLA' and label ne 'Preschool' then deleteme='Yes';
if fmtname='AGEMLB' and label = 'Preschool' then label='Kids';
if fmtname='GENDERML' and label='Farmale' then output;
output;
run;
%mp_loadformat(&cat1
,work.stagedata3
,loadtarget=YES
,auditlibds=perm.audit
,locklibds=0
,delete_col=deleteme
,outds_add=add_test1
,outds_del=del_test1
,outds_mod=mod_test1
,mdebug=1
)
%mp_assert(
iftrue=(%mf_nobs(del_test1)=2),
desc=Test 1 - deleted obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(mod_test1)=4),
desc=Test 1 - mod obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(add_test1)=1),
desc=Test 1 - add obs,
outds=work.test_results
)
/* now check the order of the notsorted format */
%mp_cntlout(libcat=&cat1,cntlout=work.cntlout2)
%let check1=0;
%let check2=0;
data test;
set work.cntlout2;
where fmtname='GENDERML';
if _n_=4 and label='Farmale' then call symputx('check1',1);
if _n_=5 and label='Farmale' then call symputx('check2',1);
run;
%mp_assert(
iftrue=(&check1=1 and &check2=1),
desc=Ensuring Farmale values retain their order,
outds=work.test_results
)