diff --git a/all.sas b/all.sas
index 329b1b5..fabe602 100644
--- a/all.sas
+++ b/all.sas
@@ -4161,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;
@@ -6014,6 +6022,7 @@ run;
options ps=max lrecl=max;
data _null_;
infile &outref;
+ if _n_=1 then putlog "# &libds" /;
input;
putlog _infile_;
run;
@@ -10119,19 +10128,20 @@ select distinct lowcase(memname)
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
SAS Macros
- @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_md5.sas
@li mp_storediffs.sas
Related Macros
@li mddl_dc_difftable.sas
@li mddl_dc_locktable.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
@@ -10164,13 +10174,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,-);
@@ -10233,16 +10236,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);
@@ -10260,6 +10271,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;
/**
@@ -10270,12 +10289,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
@@ -10284,11 +10301,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
@@ -10297,13 +10312,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)
@@ -10312,19 +10324,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 */
@@ -10372,7 +10386,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
@@ -12796,9 +12810,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);
@@ -14076,22 +14090,18 @@ ods package close;
%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'
@@ -14104,12 +14114,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;
/**
diff --git a/base/mp_cntlout.sas b/base/mp_cntlout.sas
index 8e56cc8..0329360 100644
--- a/base/mp_cntlout.sas
+++ b/base/mp_cntlout.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;
diff --git a/base/mp_ds2md.sas b/base/mp_ds2md.sas
index 7febabe..15b5401 100644
--- a/base/mp_ds2md.sas
+++ b/base/mp_ds2md.sas
@@ -95,6 +95,7 @@ run;
options ps=max lrecl=max;
data _null_;
infile &outref;
+ if _n_=1 then putlog "# &libds" /;
input;
putlog _infile_;
run;
diff --git a/base/mp_loadformat.sas b/base/mp_loadformat.sas
index 1945544..517d059 100644
--- a/base/mp_loadformat.sas
+++ b/base/mp_loadformat.sas
@@ -34,19 +34,20 @@
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
SAS Macros
- @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_md5.sas
@li mp_storediffs.sas
Related Macros
@li mddl_dc_difftable.sas
@li mddl_dc_locktable.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 +80,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 +142,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 +177,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 +195,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 +207,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 +218,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 +230,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 +292,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
diff --git a/base/mp_storediffs.sas b/base/mp_storediffs.sas
index e3714aa..631453c 100644
--- a/base/mp_storediffs.sas
+++ b/base/mp_storediffs.sas
@@ -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);
diff --git a/ddl/mddl_sas_cntlout.sas b/ddl/mddl_sas_cntlout.sas
index a204c9f..f7b3ece 100644
--- a/ddl/mddl_sas_cntlout.sas
+++ b/ddl/mddl_sas_cntlout.sas
@@ -11,22 +11,18 @@
%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'
@@ -39,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;
diff --git a/tests/base/mp_loadformat.test.sas b/tests/base/mp_loadformat.test.1.sas
similarity index 67%
rename from tests/base/mp_loadformat.test.sas
rename to tests/base/mp_loadformat.test.1.sas
index 9925784..5d14075 100644
--- a/tests/base/mp_loadformat.test.sas
+++ b/tests/base/mp_loadformat.test.1.sas
@@ -6,6 +6,7 @@
SAS Macros
@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
)
+
diff --git a/tests/base/mp_loadformat.test.2.sas b/tests/base/mp_loadformat.test.2.sas
new file mode 100644
index 0000000..e3ebb8b
--- /dev/null
+++ b/tests/base/mp_loadformat.test.2.sas
@@ -0,0 +1,128 @@
+/**
+ @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 `|` `|` `|` `|` `|
+
+ SAS Macros
+ @li mf_nobs.sas
+ @li mp_cntlout.sas
+ @li mp_loadformat.sas
+ @li mp_assert.sas
+ @li mp_assertdsobs.sas
+ @li mp_ds2md.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
+)
+
+%mp_ds2md(work.cntlout2)
\ No newline at end of file