mirror of
https://github.com/sasjs/core.git
synced 2026-01-16 04:50:05 +00:00
feat: mp_sortinplace and corresponding test. Closes #102.
This commit is contained in:
137
all.sas
137
all.sas
@@ -56,6 +56,10 @@ options noquotelenmax;
|
|||||||
|
|
||||||
@param libds library.dataset
|
@param libds library.dataset
|
||||||
@return output returns 1 or 0
|
@return output returns 1 or 0
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mf_existds.test.sas
|
||||||
|
|
||||||
@warning Untested on tables registered in metadata but not physically present
|
@warning Untested on tables registered in metadata but not physically present
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -2349,6 +2353,10 @@ Usage:
|
|||||||
|
|
||||||
%mp_assertdsobs(sashelp.class) %* tests if any observations are present;
|
%mp_assertdsobs(sashelp.class) %* tests if any observations are present;
|
||||||
|
|
||||||
|
%mp_assertdsobs(sashelp.class,test=ATLEAST 10) %* pass if >9 obs present;
|
||||||
|
|
||||||
|
%mp_assertdsobs(sashelp.class,test=ATMOST 20) %* pass if <21 obs present;
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@@ -2360,9 +2368,9 @@ Usage:
|
|||||||
@li HASOBS - Test is a PASS if the input dataset has any observations
|
@li HASOBS - Test is a PASS if the input dataset has any observations
|
||||||
@li EMPTY - Test is a PASS if input dataset is empty
|
@li EMPTY - Test is a PASS if input dataset is empty
|
||||||
@li EQUALS [integer] - Test passes if row count matches the provided integer
|
@li EQUALS [integer] - Test passes if row count matches the provided integer
|
||||||
@LI ATLEAST [integer] - Test passes if row count is more than or equal to
|
@li ATLEAST [integer] - Test passes if row count is more than or equal to
|
||||||
the provided integer
|
the provided integer
|
||||||
@LI ATMOST [integer] - Test passes if row count is less than or equal to
|
@li ATMOST [integer] - Test passes if row count is less than or equal to
|
||||||
the provided integer
|
the provided integer
|
||||||
@param [out] outds= (work.test_results) The output dataset to contain the
|
@param [out] outds= (work.test_results) The output dataset to contain the
|
||||||
results. If it does not exist, it will be created, with the following format:
|
results. If it does not exist, it will be created, with the following format:
|
||||||
@@ -7726,16 +7734,29 @@ proc sql
|
|||||||
creating a copy of the dataset (without data, WITH constraints) in the same
|
creating a copy of the dataset (without data, WITH constraints) in the same
|
||||||
library, appending a sorted view into it, and finally - renaming it.
|
library, appending a sorted view into it, and finally - renaming it.
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
Example usage:
|
||||||
@li mf_existds.sas
|
|
||||||
@li mf_getuniquename.sas
|
|
||||||
@li mp_abort.sas
|
|
||||||
|
|
||||||
<h4> Related Macros </h4>
|
proc sql;
|
||||||
@li mf_getvalue.sas
|
create table work.example as
|
||||||
|
select * from sashelp.class;
|
||||||
|
alter table work.example
|
||||||
|
add constraint pk primary key(name);
|
||||||
|
%mp_sortinplace(work.example)
|
||||||
|
|
||||||
@param [in] libds The libref.datasetname that needs to be sorted
|
@param [in] libds The libref.datasetname that needs to be sorted
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_existds.sas
|
||||||
|
@li mf_getengine.sas
|
||||||
|
@li mf_getquotedstr.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mf_nobs.sas
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mp_getpk.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_sortinplace.test.sas
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@source https://github.com/sasjs/core
|
@source https://github.com/sasjs/core
|
||||||
@@ -7745,38 +7766,74 @@ proc sql
|
|||||||
%macro mp_sortinplace(libds
|
%macro mp_sortinplace(libds
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%local lib ds tempds1 tempds2 tempvw;
|
%local lib ds tempds1 tempds2 tempvw sortkey;
|
||||||
|
|
||||||
/* perform validations */
|
/* perform validations */
|
||||||
%mp_abort(iftrue=(%sysfunc(countw(&libds,.)) ne 1)
|
%mp_abort(iftrue=(%sysfunc(countc(&libds,.)) ne 1)
|
||||||
,mac=&sysmacroname
|
,mac=mp_sortinplace
|
||||||
,msg=%str(LIBDS (&libds) should have LIBREF.DATASET format)
|
,msg=%str(LIBDS (&libds) should have LIBREF.DATASET format)
|
||||||
)
|
)
|
||||||
%mp_abort(iftrue=(%mf_existds(&libds)=0)
|
%mp_abort(iftrue=(%mf_existds(&libds)=0)
|
||||||
,mac=&sysmacroname
|
,mac=mp_sortinplace
|
||||||
,msg=%str(&libds does not exist)
|
,msg=%str(&libds does not exist)
|
||||||
)
|
)
|
||||||
|
|
||||||
%let lib=%scan(&libds,1,.);
|
%let lib=%scan(&libds,1,.);
|
||||||
%let ds=%scan(&libds,2,.);
|
%let ds=%scan(&libds,2,.);
|
||||||
%mp_abort(iftrue=(&lib ne V9)
|
%mp_abort(iftrue=(%mf_getengine(&lib) ne V9)
|
||||||
,mac=&sysmacroname
|
,mac=mp_sortinplace
|
||||||
,msg=%str(&lib is not a BASE engine library)
|
,msg=%str(&lib is not a BASE engine library)
|
||||||
)
|
)
|
||||||
|
|
||||||
/* grab a copy of the constraints so we know what to sort by */
|
/* grab a copy of the constraints so we know what to sort by */
|
||||||
%let tempds1=%mf_getuniquename(prefix=&sysmacroname);
|
%let tempds1=%mf_getuniquename(prefix=&sysmacroname);
|
||||||
%mp_getconstraints(lib=&lib,ds=example,outds=work.&tempds1)
|
%mp_getpk(lib=&lib,ds=&ds,outds=work.&tempds1)
|
||||||
|
|
||||||
/* create empty copy, WITH constraints, in the same library */
|
%if %mf_nobs(work.&tempds1)=0 %then %do;
|
||||||
|
%put &sysmacroname: No PK found in &lib..&ds;
|
||||||
|
%put Sorting will not take place;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
set work.&tempds1;
|
||||||
|
call symputx('sortkey',pk_fields);
|
||||||
|
run;
|
||||||
|
|
||||||
|
|
||||||
|
/* create empty copy, with ALL constraints, in the same library */
|
||||||
%let tempds2=%mf_getuniquename(prefix=&sysmacroname);
|
%let tempds2=%mf_getuniquename(prefix=&sysmacroname);
|
||||||
proc append base=&lib..&tempds2 data=&libds(obs=0);
|
proc append base=&lib..&tempds2 data=&libds(obs=0);
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
/* create sorted view */
|
||||||
%let tempvw=%mf_getuniquename(prefix=&sysmacroname);
|
%let tempvw=%mf_getuniquename(prefix=&sysmacroname);
|
||||||
proc sql;
|
proc sql;
|
||||||
|
create view work.&tempvw as select * from &lib..&ds
|
||||||
|
order by %mf_getquotedstr(&sortkey,quote=%str());
|
||||||
|
|
||||||
|
/* append sorted data */
|
||||||
|
proc append base=&lib..&tempds2 data=work.&tempvw;
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* do validations */
|
||||||
|
%mp_abort(iftrue=(&syscc ne 0)
|
||||||
|
,mac=mp_sortinplace
|
||||||
|
,msg=%str(syscc=&syscc prior to replace operation)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue=(%mf_nobs(&lib..&tempds2) ne %mf_nobs(&lib..&ds))
|
||||||
|
,mac=mp_sortinplace
|
||||||
|
,msg=%str(new dataset has a different number of logical obs to the old)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* drop old dataset */
|
||||||
|
proc sql;
|
||||||
|
drop table &lib..&ds;
|
||||||
|
|
||||||
|
/* rename the new dataset */
|
||||||
|
proc datasets library=&lib;
|
||||||
|
change &tempds2=&ds;
|
||||||
|
run;
|
||||||
|
|
||||||
|
|
||||||
%mend mp_sortinplace;/**
|
%mend mp_sortinplace;/**
|
||||||
@@ -8480,19 +8537,23 @@ run;
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url
|
||||||
|
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
|
|
||||||
%mp_unzip(ziploc="/some/file.zip",outdir=/some/folder)
|
%mp_unzip(ziploc="/some/file.zip",outdir=/some/folder)
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
More info: https://blogs.sas.com/content/sasdummy/2015/05/11/using-filename-zip-to-unzip-and-read-data-files-in-sas/
|
||||||
@li mf_mkdir.sas
|
|
||||||
@li mf_getuniquefileref.sas
|
|
||||||
|
|
||||||
@param ziploc= Fileref or quoted full path to zip file ("/path/to/file.zip")
|
@param ziploc= Fileref or quoted full path to zip file ("/path/to/file.zip")
|
||||||
@param outdir= (%sysfunc(pathname(work))) Directory in which to write the
|
@param outdir= (%sysfunc(pathname(work))) Directory in which to write the
|
||||||
outputs (created if non existant)
|
outputs (created if non existant)
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_mkdir.sas
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mp_binarycopy.sas
|
||||||
|
|
||||||
@version 9.4
|
@version 9.4
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@source https://github.com/sasjs/core
|
@source https://github.com/sasjs/core
|
||||||
@@ -8504,14 +8565,16 @@ run;
|
|||||||
,outdir=%sysfunc(pathname(work))
|
,outdir=%sysfunc(pathname(work))
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%local f1 f2 f3;
|
%local f1 f2 ;
|
||||||
%let f1=%mf_getuniquefileref();
|
%let f1=%mf_getuniquefileref();
|
||||||
%let f2=%mf_getuniquefileref();
|
%let f2=%mf_getuniquefileref();
|
||||||
%let f3=%mf_getuniquefileref();
|
|
||||||
|
|
||||||
/* Macro variable &datazip would be read from the file */
|
/* Macro variable &datazip would be read from the file */
|
||||||
filename &f1 ZIP &ziploc;
|
filename &f1 ZIP &ziploc;
|
||||||
|
|
||||||
|
/* create target folder */
|
||||||
|
%mf_mkdir(&outdir)
|
||||||
|
|
||||||
/* Read the "members" (files) from the ZIP file */
|
/* Read the "members" (files) from the ZIP file */
|
||||||
data _data_(keep=memname isFolder);
|
data _data_(keep=memname isFolder);
|
||||||
length memname $200 isFolder 8;
|
length memname $200 isFolder 8;
|
||||||
@@ -8526,24 +8589,36 @@ data _data_(keep=memname isFolder);
|
|||||||
end;
|
end;
|
||||||
rc=dclose(fid);
|
rc=dclose(fid);
|
||||||
run;
|
run;
|
||||||
filename &f1 clear;
|
|
||||||
|
filename &f2 temp;
|
||||||
|
|
||||||
/* loop through each entry and either create the subfolder or extract member */
|
/* loop through each entry and either create the subfolder or extract member */
|
||||||
%mf_mkdir(&outdir)
|
|
||||||
data _null_;
|
data _null_;
|
||||||
set &syslast;
|
set &syslast;
|
||||||
|
file &f2;
|
||||||
if isFolder then call execute('%mf_mkdir(&outdir/'!!memname!!')');
|
if isFolder then call execute('%mf_mkdir(&outdir/'!!memname!!')');
|
||||||
else do;
|
else do;
|
||||||
call execute(
|
qname=quote(cats("&outdir/",memname));
|
||||||
cats('filename &f2 zip &ziploc member="',memname,'" recfm=n;')
|
bname=cats('(',memname,')');
|
||||||
);
|
put '/* hat tip: "data _null_" on SAS-L */';
|
||||||
call execute('filename &f3 "&outdir/'!!trim(memname)!!'" recfm=n;');
|
put 'data _null_;';
|
||||||
call execute('data _null_; rc=fcopy("&f2","&f3");run;');
|
put ' infile &f1 ' bname ' lrecl=256 recfm=F length=length eof=eof unbuf;';
|
||||||
call execute('filename &f2 clear; filename &f3 clear;');
|
put ' file ' qname ' lrecl=256 recfm=N;';
|
||||||
|
put ' input;';
|
||||||
|
put ' put _infile_ $varying256. length;';
|
||||||
|
put ' return;';
|
||||||
|
put 'eof:';
|
||||||
|
put ' stop;';
|
||||||
|
put 'run;';
|
||||||
end;
|
end;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%mend mp_unzip;/**
|
%inc &f2/source2;
|
||||||
|
|
||||||
|
filename &f2 clear;
|
||||||
|
|
||||||
|
%mend mp_unzip;
|
||||||
|
/**
|
||||||
@file mp_updatevarlength.sas
|
@file mp_updatevarlength.sas
|
||||||
@brief Change the length of a variable
|
@brief Change the length of a variable
|
||||||
@details The library is assumed to be assigned. Simple character updates
|
@details The library is assumed to be assigned. Simple character updates
|
||||||
|
|||||||
@@ -13,8 +13,20 @@
|
|||||||
creating a copy of the dataset (without data, WITH constraints) in the same
|
creating a copy of the dataset (without data, WITH constraints) in the same
|
||||||
library, appending a sorted view into it, and finally - renaming it.
|
library, appending a sorted view into it, and finally - renaming it.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
proc sql;
|
||||||
|
create table work.example as
|
||||||
|
select * from sashelp.class;
|
||||||
|
alter table work.example
|
||||||
|
add constraint pk primary key(name);
|
||||||
|
%mp_sortinplace(work.example)
|
||||||
|
|
||||||
|
@param [in] libds The libref.datasetname that needs to be sorted
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_existds.sas
|
@li mf_existds.sas
|
||||||
|
@li mf_getengine.sas
|
||||||
@li mf_getquotedstr.sas
|
@li mf_getquotedstr.sas
|
||||||
@li mf_getuniquename.sas
|
@li mf_getuniquename.sas
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
@@ -22,9 +34,7 @@
|
|||||||
@li mp_getpk.sas
|
@li mp_getpk.sas
|
||||||
|
|
||||||
<h4> Related Macros </h4>
|
<h4> Related Macros </h4>
|
||||||
@li mf_getvalue.sas
|
@li mp_sortinplace.test.sas
|
||||||
|
|
||||||
@param [in] libds The libref.datasetname that needs to be sorted
|
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -38,19 +48,19 @@
|
|||||||
%local lib ds tempds1 tempds2 tempvw sortkey;
|
%local lib ds tempds1 tempds2 tempvw sortkey;
|
||||||
|
|
||||||
/* perform validations */
|
/* perform validations */
|
||||||
%mp_abort(iftrue=(%sysfunc(countw(&libds,.)) ne 1)
|
%mp_abort(iftrue=(%sysfunc(countc(&libds,.)) ne 1)
|
||||||
,mac=&sysmacroname
|
,mac=mp_sortinplace
|
||||||
,msg=%str(LIBDS (&libds) should have LIBREF.DATASET format)
|
,msg=%str(LIBDS (&libds) should have LIBREF.DATASET format)
|
||||||
)
|
)
|
||||||
%mp_abort(iftrue=(%mf_existds(&libds)=0)
|
%mp_abort(iftrue=(%mf_existds(&libds)=0)
|
||||||
,mac=&sysmacroname
|
,mac=mp_sortinplace
|
||||||
,msg=%str(&libds does not exist)
|
,msg=%str(&libds does not exist)
|
||||||
)
|
)
|
||||||
|
|
||||||
%let lib=%scan(&libds,1,.);
|
%let lib=%scan(&libds,1,.);
|
||||||
%let ds=%scan(&libds,2,.);
|
%let ds=%scan(&libds,2,.);
|
||||||
%mp_abort(iftrue=(&lib ne V9)
|
%mp_abort(iftrue=(%mf_getengine(&lib) ne V9)
|
||||||
,mac=&sysmacroname
|
,mac=mp_sortinplace
|
||||||
,msg=%str(&lib is not a BASE engine library)
|
,msg=%str(&lib is not a BASE engine library)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -87,7 +97,7 @@ run;
|
|||||||
|
|
||||||
/* do validations */
|
/* do validations */
|
||||||
%mp_abort(iftrue=(&syscc ne 0)
|
%mp_abort(iftrue=(&syscc ne 0)
|
||||||
,mac=&sysmacroname
|
,mac=mp_sortinplace
|
||||||
,msg=%str(syscc=&syscc prior to replace operation)
|
,msg=%str(syscc=&syscc prior to replace operation)
|
||||||
)
|
)
|
||||||
%mp_abort(iftrue=(%mf_nobs(&lib..&tempds2) ne %mf_nobs(&lib..&ds))
|
%mp_abort(iftrue=(%mf_nobs(&lib..&tempds2) ne %mf_nobs(&lib..&ds))
|
||||||
|
|||||||
42
tests/crossplatform/mp_sortinplace.test.sas
Normal file
42
tests/crossplatform/mp_sortinplace.test.sas
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_sortinplace.test.sas
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_sortinplace.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
@li mp_assertdsobs.sas
|
||||||
|
@li mp_getconstraints.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
/** Test 1 - regular usage */
|
||||||
|
proc sql;
|
||||||
|
create table work.example as
|
||||||
|
select * from sashelp.classfit;
|
||||||
|
alter table work.example
|
||||||
|
add constraint pk primary key(name);
|
||||||
|
%mp_sortinplace(work.example)
|
||||||
|
|
||||||
|
%mp_getconstraints(lib=work,ds=example,outds=work.testme)
|
||||||
|
|
||||||
|
%mp_assertdsobs(work.testme,
|
||||||
|
desc=Test1 - check constraints recreated,
|
||||||
|
test=EQUALS 1,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%let test1=0;
|
||||||
|
data _null_;
|
||||||
|
set work.example;
|
||||||
|
call symputx('test1',name);
|
||||||
|
stop;
|
||||||
|
run;
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
%str(&test1)=%str(Alfred)
|
||||||
|
),
|
||||||
|
desc=Check if sort was appplied,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user