mirror of
https://github.com/sasjs/core.git
synced 2026-01-03 23:50:06 +00:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ce7efee3e | ||
|
|
357677e45c | ||
|
|
a4a332926e | ||
|
|
0a29006914 | ||
|
|
0885bad859 | ||
|
|
42bd1750bd | ||
|
|
58784b2f28 | ||
|
|
e125aace9b | ||
|
|
b02a9e3478 | ||
|
|
3d3c76c836 | ||
|
|
e039f1cd83 | ||
|
|
6c8165601d | ||
|
|
596624c1bf | ||
|
|
4aca34d4c2 | ||
|
|
858b378658 | ||
|
|
8af41a8cc3 | ||
|
|
b13c33cbde | ||
|
|
6906f025d6 | ||
|
|
8b355b8056 | ||
|
|
8938553588 | ||
|
|
14852f3647 | ||
|
|
b55e91784d | ||
|
|
fc14aaa37f | ||
|
|
3295f3845e | ||
|
|
bbf734fbf6 | ||
|
|
c4e9ab7b3e | ||
|
|
11c181ef8b | ||
|
|
1d0754d705 | ||
|
|
80acecd3e6 | ||
|
|
cb2a8db087 | ||
|
|
17b58d775a | ||
|
|
a801e5c1f1 | ||
|
|
966f2cf78d | ||
|
|
8c21b9397f | ||
|
|
6056b739bf | ||
|
|
7823933cf7 | ||
|
|
7623abb1f7 | ||
|
|
f7335b78c3 |
@@ -1,2 +1,11 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
sasjs lint
|
sasjs lint
|
||||||
|
|
||||||
|
# Avoid commits to the master branch
|
||||||
|
BRANCH=`git rev-parse --abbrev-ref HEAD`
|
||||||
|
|
||||||
|
if [[ "$BRANCH" =~ ^(master|main|develop)$ ]]; then
|
||||||
|
echo "You are on branch $BRANCH. Are you sure you want to commit to this branch?"
|
||||||
|
echo "If so, commit with -n to bypass the pre-commit hook."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Copyright 2020 (Allan Bowe)
|
Copyright 2021 (Allan Bowe)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
|||||||
16
README.md
16
README.md
@@ -1,7 +1,7 @@
|
|||||||
# Macro Core
|
# Macro Core
|
||||||
[![npm package][npm-image]][npm-url]
|
[![npm package][npm-image]][npm-url]
|
||||||
[![Github Workflow][githubworkflow-image]][githubworkflow-url]
|
[![Github Workflow][githubworkflow-image]][githubworkflow-url]
|
||||||
[]()
|

|
||||||

|

|
||||||
[](/LICENSE)
|
[](/LICENSE)
|
||||||

|

|
||||||
@@ -31,21 +31,21 @@ Documentation: https://core.sasjs.io
|
|||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
### **base** library (SAS9/Viya)
|
### BASE library (SAS9/Viya)
|
||||||
|
|
||||||
- OS independent
|
- OS independent
|
||||||
- Not metadata aware
|
- Not metadata aware
|
||||||
- No X command
|
- No X command
|
||||||
- Prefixes: _mf_, _mp_
|
- Prefixes: _mf_, _mp_
|
||||||
|
|
||||||
#### **fcmp** library (SAS9/Viya)
|
#### FCMP library (SAS9/Viya)
|
||||||
- Function and macro names are identical, except for special cases
|
- Function and macro names are identical, except for special cases
|
||||||
- Prefixes: _mcf_
|
- Prefixes: _mcf_
|
||||||
|
|
||||||
The fcmp macros are used to generate fcmp functions, and can be used with or
|
The fcmp macros are used to generate fcmp functions, and can be used with or
|
||||||
without the `proc fcmp` wrapper.
|
without the `proc fcmp` wrapper.
|
||||||
|
|
||||||
### **meta** library (SAS9 only)
|
### META library (SAS9 only)
|
||||||
|
|
||||||
Macros used in SAS EBI, which connect to the metadata server.
|
Macros used in SAS EBI, which connect to the metadata server.
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ Macros used in SAS EBI, which connect to the metadata server.
|
|||||||
- No X command
|
- No X command
|
||||||
- Prefixes: _mm_
|
- Prefixes: _mm_
|
||||||
|
|
||||||
### **server** library (@sasjs/server only)
|
### SERVER library (@sasjs/server only)
|
||||||
These macros are used for building applications using [@sasjs/server](https://server.sasjs.io) - an open source REST API for Desktop SAS.
|
These macros are used for building applications using [@sasjs/server](https://server.sasjs.io) - an open source REST API for Desktop SAS.
|
||||||
|
|
||||||
- OS independent
|
- OS independent
|
||||||
@@ -62,7 +62,7 @@ These macros are used for building applications using [@sasjs/server](https://se
|
|||||||
- No X command
|
- No X command
|
||||||
- Prefixes: _ms_
|
- Prefixes: _ms_
|
||||||
|
|
||||||
### **viya** library (Viya only)
|
### VIYA library (Viya only)
|
||||||
|
|
||||||
Macros used for interfacing with SAS Viya.
|
Macros used for interfacing with SAS Viya.
|
||||||
|
|
||||||
@@ -70,14 +70,14 @@ Macros used for interfacing with SAS Viya.
|
|||||||
- No X command
|
- No X command
|
||||||
- Prefixes: _mv_, _mvf_
|
- Prefixes: _mv_, _mvf_
|
||||||
|
|
||||||
### **metax** library (SAS9 only)
|
### METAX library (SAS9 only)
|
||||||
|
|
||||||
- OS specific
|
- OS specific
|
||||||
- Metadata aware
|
- Metadata aware
|
||||||
- X command enabled
|
- X command enabled
|
||||||
- Prefixes: _mmw_,_mmu_,_mmx_
|
- Prefixes: _mmw_,_mmu_,_mmx_
|
||||||
|
|
||||||
### **lua** library
|
### LUA library
|
||||||
|
|
||||||
Wait - this is a macro library - what is LUA doing here? Well, it is a little known fact that you CAN run LUA within a SAS Macro. It has to be written to a text file with a `.lua` extension, from where you can `%include` it. So, without using the `proc lua` wrapper.
|
Wait - this is a macro library - what is LUA doing here? Well, it is a little known fact that you CAN run LUA within a SAS Macro. It has to be written to a text file with a `.lua` extension, from where you can `%include` it. So, without using the `proc lua` wrapper.
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,10 @@
|
|||||||
|
|
||||||
@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
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
Run without arguments to see a list of detectable features.
|
Run without arguments to see a list of detectable features.
|
||||||
Note - this list is based on known versions of SAS rather than
|
Note - this list is based on known versions of SAS rather than
|
||||||
actual feature detection, as that is tricky / impossible to do
|
actual feature detection, as that is tricky / impossible to do
|
||||||
without generating errors in most cases.
|
without generating errs in most cases.
|
||||||
|
|
||||||
%put %mf_existfeature(PROCLUA);
|
%put %mf_existfeature(PROCLUA);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
@param attr full list in [documentation](
|
@param attr full list in [documentation](
|
||||||
https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000147794.htm)
|
https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000147794.htm)
|
||||||
@return output returns result of the attrc value supplied, or -1 and log
|
@return output returns result of the attrc value supplied, or -1 and log
|
||||||
message if error.
|
message if err.
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
@param attr Common values are NLOBS and NVARS, full list in [documentation](
|
@param attr Common values are NLOBS and NVARS, full list in [documentation](
|
||||||
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000212040.htm)
|
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000212040.htm)
|
||||||
@return output returns result of the attrn value supplied, or -1 and log
|
@return output returns result of the attrn value supplied, or -1 and log
|
||||||
message if error.
|
message if err.
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|||||||
@@ -15,12 +15,12 @@
|
|||||||
for:
|
for:
|
||||||
> "these","words","are","double","quoted"
|
> "these","words","are","double","quoted"
|
||||||
|
|
||||||
@param in_str the unquoted, spaced delimited string to transform
|
@param [in] in_str The unquoted, spaced delimited string to transform
|
||||||
@param dlm= the delimeter to be applied to the output (default comma)
|
@param [in] dlm= The delimeter to be applied to the output (default comma)
|
||||||
@param indlm= the delimeter used for the input (default is space)
|
@param [in] indlm= (,) The delimeter used for the input (default is space)
|
||||||
@param quote= the quote mark to apply (S=Single, D=Double). If any other value
|
@param [in] quote= (S) The quote mark to apply (S=Single, D=Double, N=None).
|
||||||
than uppercase S or D is supplied, then that value will be used as the
|
If any other value than uppercase S or D is supplied, then that value will
|
||||||
quoting character.
|
be used as the quoting character.
|
||||||
@return output returns a string with the newly quoted / delimited output.
|
@return output returns a string with the newly quoted / delimited output.
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@@ -30,9 +30,10 @@
|
|||||||
|
|
||||||
%macro mf_getquotedstr(IN_STR,DLM=%str(,),QUOTE=S,indlm=%str( )
|
%macro mf_getquotedstr(IN_STR,DLM=%str(,),QUOTE=S,indlm=%str( )
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
%if "e=S %then %let quote=%str(%');
|
/* credit Rowland Hale - byte34 is double quote, 39 is single quote */
|
||||||
%else %if "e=D %then %let quote=%str(%");
|
%if "e=S %then %let quote=%qsysfunc(byte(39));
|
||||||
%else %let quote=%str();
|
%else %if "e=D %then %let quote=%qsysfunc(byte(34));
|
||||||
|
%else %if "e=N %then %let quote=;
|
||||||
%local i item buffer;
|
%local i item buffer;
|
||||||
%let i=1;
|
%let i=1;
|
||||||
%do %while (%qscan(&IN_STR,&i,%str(&indlm)) ne %str() ) ;
|
%do %while (%qscan(&IN_STR,&i,%str(&indlm)) ne %str() ) ;
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getattrn.sas
|
@li mf_getattrn.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_setkeyvalue.sas
|
||||||
|
|
||||||
@param libds dataset to query
|
@param libds dataset to query
|
||||||
@param variable the variable which contains the value to return.
|
@param variable the variable which contains the value to return.
|
||||||
@param filter contents of where clause
|
@param filter contents of where clause
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ Usage:
|
|||||||
%end;
|
%end;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Now create the directory. Complain loudly of any errors.
|
Now create the directory. Complain loudly of any errs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
%let dname = %sysfunc(dcreate(&child, &parent));
|
%let dname = %sysfunc(dcreate(&child, &parent));
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
@param libds library.dataset
|
@param libds library.dataset
|
||||||
|
|
||||||
@return output returns result of the attrn value supplied, or log message
|
@return output returns result of the attrn value supplied, or log message
|
||||||
if error.
|
if err.
|
||||||
|
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
|
|
||||||
%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
|
||||||
@@ -19,9 +23,9 @@
|
|||||||
@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:
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
|
|
||||||
/* create folders and copy content */
|
/* create folders and copy content */
|
||||||
data _null_;
|
data _null_;
|
||||||
|
length msg $200;
|
||||||
|
call missing(msg);
|
||||||
set work.&tempds;
|
set work.&tempds;
|
||||||
if _n_ = 1 then dpos+sum(length(directory),2);
|
if _n_ = 1 then dpos+sum(length(directory),2);
|
||||||
filepath2="&target/"!!substr(filepath,dpos);
|
filepath2="&target/"!!substr(filepath,dpos);
|
||||||
@@ -63,9 +65,9 @@
|
|||||||
rc1=filename(fref1,filepath,'disk','recfm=n');
|
rc1=filename(fref1,filepath,'disk','recfm=n');
|
||||||
rc2=filename(fref2,filepath2,'disk','recfm=n');
|
rc2=filename(fref2,filepath2,'disk','recfm=n');
|
||||||
if fcopy(fref1,fref2) ne 0 then do;
|
if fcopy(fref1,fref2) ne 0 then do;
|
||||||
sysmsg=sysmsg();
|
msg=sysmsg();
|
||||||
putlog "%str(ERR)OR: Unable to copy " filepath " to " filepath2;
|
putlog "%str(ERR)OR: Unable to copy " filepath " to " filepath2;
|
||||||
putlog sysmg=;
|
putlog msg=;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
rc=filename(fref1);
|
rc=filename(fref1);
|
||||||
|
|||||||
@@ -17,8 +17,9 @@
|
|||||||
@li mf_isblank.sas
|
@li mf_isblank.sas
|
||||||
|
|
||||||
|
|
||||||
@param list space separated list of datasets / views, WITHOUT libref
|
@param [in] list space separated list of datasets / views, WITHOUT libref
|
||||||
@param libref= (WORK) Note - you can only drop from a single library at a time
|
@param [in] libref= (WORK) Note - you can only drop from one library at a time
|
||||||
|
@param [in] iftrue= (1=1) Conditionally drop tables, eg if &debug=N
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -28,8 +29,11 @@
|
|||||||
%macro mp_dropmembers(
|
%macro mp_dropmembers(
|
||||||
list /* space separated list of datasets / views */
|
list /* space separated list of datasets / views */
|
||||||
,libref=WORK /* can only drop from a single library at a time */
|
,libref=WORK /* can only drop from a single library at a time */
|
||||||
|
,iftrue=%str(1=1)
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||||
|
|
||||||
%if %mf_isblank(&list) %then %do;
|
%if %mf_isblank(&list) %then %do;
|
||||||
%put NOTE: nothing to drop!;
|
%put NOTE: nothing to drop!;
|
||||||
%return;
|
%return;
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Converts every value in a dataset to it's formatted value
|
@brief Converts every value in a dataset to formatted value
|
||||||
@details Converts every value to it's formatted value. All variables will
|
@details Converts every value to it's formatted value. All variables will
|
||||||
become character, and will be in the same order.
|
become character, and will be in the same order as the original dataset.
|
||||||
|
|
||||||
|
Lengths will be adjusted according to the format lengths, where applicable.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%mp_ds2fmtds(sashelp.cars,work.cars)
|
%mp_ds2fmtds(sashelp.cars,work.cars)
|
||||||
|
%mp_ds2fmtds(sashelp.vallopt,vw_vallopt)
|
||||||
|
|
||||||
@param [in] libds The library.dataset to be converted
|
@param [in] libds The library.dataset to be converted
|
||||||
@param [out] outds The dataset to create.
|
@param [out] outds The dataset to create.
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_existds.sas
|
||||||
|
|
||||||
<h4> Related Macros <h4>
|
<h4> Related Macros <h4>
|
||||||
@li mp_jsonout.sas
|
@li mp_jsonout.sas
|
||||||
|
|
||||||
@@ -22,8 +28,9 @@
|
|||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
/* validations */
|
/* validations */
|
||||||
%if not %sysfunc(exist(&libds)) %then %do;
|
|
||||||
%put %str(WARN)ING: &libds does not exist;
|
%if not %mf_existds(libds=&libds) %then %do;
|
||||||
|
%put %str(WARN)ING: &libds does not exist as either a VIEW or DATASET;
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
%if %index(&libds,.)=0 %then %let libds=WORK.&libds;
|
%if %index(&libds,.)=0 %then %let libds=WORK.&libds;
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ data _null_;
|
|||||||
if _n_>&maxobs then stop;
|
if _n_>&maxobs then stop;
|
||||||
%end;
|
%end;
|
||||||
length _____str $32767;
|
length _____str $32767;
|
||||||
|
call missing(_____str);
|
||||||
format _numeric_ best.;
|
format _numeric_ best.;
|
||||||
format _character_ ;
|
format _character_ ;
|
||||||
%local i comma var vtype vfmt;
|
%local i comma var vtype vfmt;
|
||||||
|
|||||||
@@ -33,8 +33,8 @@
|
|||||||
@param [in] targetds The target dataset against which to verify the query
|
@param [in] targetds The target dataset against which to verify the query
|
||||||
@param [out] abort= (YES) If YES will call mp_abort.sas on any exceptions
|
@param [out] abort= (YES) If YES will call mp_abort.sas on any exceptions
|
||||||
@param [out] outds= (work.mp_filtervalidate) Output dataset containing the
|
@param [out] outds= (work.mp_filtervalidate) Output dataset containing the
|
||||||
error / warning message, if one exists. If this table contains any rows,
|
err / warning message, if one exists. If this table contains any rows,
|
||||||
there are problems!
|
there are problems!
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ data &outds(keep=name type length varnum format label ddtype);
|
|||||||
else if formatd=0 then format=cats(format2,formatl,'.');
|
else if formatd=0 then format=cats(format2,formatl,'.');
|
||||||
else format=cats(format2,formatl,'.',formatd);
|
else format=cats(format2,formatl,'.',formatd);
|
||||||
type='N';
|
type='N';
|
||||||
if format=:'DATETIME' then ddtype='DATETIME';
|
if format=:'DATETIME' or format=:'E8601DT' then ddtype='DATETIME';
|
||||||
else if format=:'DATE' or format=:'DDMMYY' or format=:'MMDDYY'
|
else if format=:'DATE' or format=:'DDMMYY' or format=:'MMDDYY'
|
||||||
or format=:'YYMMDD' or format=:'E8601DA' or format=:'B8601DA'
|
or format=:'YYMMDD' or format=:'E8601DA' or format=:'B8601DA'
|
||||||
or format=:'MONYY'
|
or format=:'MONYY'
|
||||||
|
|||||||
@@ -4,24 +4,27 @@
|
|||||||
@details Useful for capturing constraints before they are dropped / reapplied
|
@details Useful for capturing constraints before they are dropped / reapplied
|
||||||
during an update.
|
during an update.
|
||||||
|
|
||||||
proc sql;
|
proc sql;
|
||||||
create table work.example(
|
create table work.example(
|
||||||
TX_FROM float format=datetime19.,
|
TX_FROM float format=datetime19.,
|
||||||
DD_TYPE char(16),
|
DD_TYPE char(16),
|
||||||
DD_SOURCE char(2048),
|
DD_SOURCE char(2048),
|
||||||
DD_SHORTDESC char(256),
|
DD_SHORTDESC char(256),
|
||||||
constraint pk primary key(tx_from, dd_type,dd_source),
|
constraint pk primary key(tx_from, dd_type,dd_source),
|
||||||
constraint unq unique(tx_from, dd_type),
|
constraint unq unique(tx_from, dd_type),
|
||||||
constraint nnn not null(DD_SHORTDESC)
|
constraint nnn not null(DD_SHORTDESC)
|
||||||
);
|
);
|
||||||
|
|
||||||
%mp_getconstraints(lib=work,ds=example,outds=work.constraints)
|
%mp_getconstraints(lib=work,ds=example,outds=work.constraints)
|
||||||
|
|
||||||
@param lib= The target library (default=WORK)
|
@param [in] lib= (WORK) The target library
|
||||||
@param ds= The target dataset. Leave blank (default) for all datasets.
|
@param [in] ds= The target dataset. Leave blank (default) for all datasets.
|
||||||
@param outds the output dataset
|
@param [in] mdebug= (0) Set to 1 to preserve temp tables, print var values etc
|
||||||
|
@param [out] outds= (mp_getconstraints) the output dataset
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mp_dropmembers.sas
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -31,11 +34,33 @@
|
|||||||
%macro mp_getconstraints(lib=WORK
|
%macro mp_getconstraints(lib=WORK
|
||||||
,ds=
|
,ds=
|
||||||
,outds=mp_getconstraints
|
,outds=mp_getconstraints
|
||||||
|
,mdebug=0
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%let lib=%upcase(&lib);
|
%let lib=%upcase(&lib);
|
||||||
%let ds=%upcase(&ds);
|
%let ds=%upcase(&ds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Neither dictionary tables nor sashelp provides a constraint order column,
|
||||||
|
* however they DO arrive in the correct order. So, create the col.
|
||||||
|
**/
|
||||||
|
%local vw;
|
||||||
|
%let vw=%mf_getuniquename(prefix=mp_getconstraints_vw_);
|
||||||
|
data &vw /view=&vw;
|
||||||
|
set sashelp.vcncolu;
|
||||||
|
where table_catalog="&lib";
|
||||||
|
|
||||||
|
/* use retain approach to reset the constraint order with each constraint */
|
||||||
|
length tmp $1000;
|
||||||
|
retain tmp;
|
||||||
|
drop tmp;
|
||||||
|
if tmp ne catx('|',table_catalog,table_name,constraint_name) then do;
|
||||||
|
constraint_order=1;
|
||||||
|
end;
|
||||||
|
else constraint_order+1;
|
||||||
|
tmp=catx('|',table_catalog, table_name,constraint_name);
|
||||||
|
run;
|
||||||
|
|
||||||
/* must use SQL as proc datasets does not support length changes */
|
/* must use SQL as proc datasets does not support length changes */
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
create table &outds as
|
create table &outds as
|
||||||
@@ -44,8 +69,9 @@ create table &outds as
|
|||||||
,a.constraint_type
|
,a.constraint_type
|
||||||
,a.constraint_name
|
,a.constraint_name
|
||||||
,b.column_name
|
,b.column_name
|
||||||
|
,b.constraint_order
|
||||||
from dictionary.TABLE_CONSTRAINTS a
|
from dictionary.TABLE_CONSTRAINTS a
|
||||||
left join dictionary.constraint_column_usage b
|
left join &vw b
|
||||||
on upcase(a.TABLE_CATALOG)=upcase(b.TABLE_CATALOG)
|
on upcase(a.TABLE_CATALOG)=upcase(b.TABLE_CATALOG)
|
||||||
and upcase(a.TABLE_NAME)=upcase(b.TABLE_NAME)
|
and upcase(a.TABLE_NAME)=upcase(b.TABLE_NAME)
|
||||||
and a.constraint_name=b.constraint_name
|
and a.constraint_name=b.constraint_name
|
||||||
@@ -58,6 +84,13 @@ create table &outds as
|
|||||||
and upcase(a.TABLE_NAME)="&ds"
|
and upcase(a.TABLE_NAME)="&ds"
|
||||||
and upcase(b.TABLE_NAME)="&ds"
|
and upcase(b.TABLE_NAME)="&ds"
|
||||||
%end;
|
%end;
|
||||||
|
order by libref, table_name, constraint_name, constraint_order
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/* tidy up */
|
||||||
|
%mp_dropmembers(
|
||||||
|
&vw,
|
||||||
|
iftrue=(&mdebug=0)
|
||||||
|
)
|
||||||
|
|
||||||
%mend mp_getconstraints;
|
%mend mp_getconstraints;
|
||||||
@@ -139,7 +139,7 @@ run;
|
|||||||
%let curds=%scan(&dsnlist,&x);
|
%let curds=%scan(&dsnlist,&x);
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
length nm lab $1024 typ $20;
|
length lab $1024 typ $20;
|
||||||
set &colinfo (where=(upcase(memname)="&curds")) end=last;
|
set &colinfo (where=(upcase(memname)="&curds")) end=last;
|
||||||
|
|
||||||
if _n_=1 then do;
|
if _n_=1 then do;
|
||||||
|
|||||||
259
base/mp_getpk.sas
Normal file
259
base/mp_getpk.sas
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Extract the primary key fields from a table or library
|
||||||
|
@details Examines the constraints to identify primary key fields - indicated
|
||||||
|
by an explicit PK constraint, or a unique index that is also NOT NULL.
|
||||||
|
|
||||||
|
Can be executed at both table and library level. Supports both BASE engine
|
||||||
|
libraries and SQL Server.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
proc sql;
|
||||||
|
create table work.example(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
DD_TYPE char(16),
|
||||||
|
DD_SOURCE char(2048),
|
||||||
|
DD_SHORTDESC char(256),
|
||||||
|
constraint pk primary key(tx_from, dd_type,dd_source),
|
||||||
|
constraint unq unique(tx_from, dd_type),
|
||||||
|
constraint nnn not null(DD_SHORTDESC)
|
||||||
|
);
|
||||||
|
%mp_getpk(work,ds=example)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
|libref:$8.|dsn:$32.|memtype:$8.|dbms_memtype:$32.|typemem:$8.|memlabel:$256.|nvar:best.|compress:$8.|pk_fields:$512.|
|
||||||
|
|---|---|---|---|---|---|---|---|---|
|
||||||
|
|WORK|EXAMPLE|DATA| |DATA| |4|NO|TX_FROM DD_TYPE DD_SOURCE|
|
||||||
|
|
||||||
|
|
||||||
|
@param [in] lib The libref to examine
|
||||||
|
@param [in] ds= (0) Select the dataset to examine, else use 0 for all tables
|
||||||
|
@param [in] mdebug= (0) Set to 1 to preserve temp tables, print var values etc
|
||||||
|
@param [out] outds= (work.mp_getpk) The name of the output table to create.
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getengine.sas
|
||||||
|
@li mf_getschema.sas
|
||||||
|
@li mp_dropmembers.sas
|
||||||
|
@li mp_getconstraints.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_getpk.test.sas
|
||||||
|
@li mp_guesspk.sas
|
||||||
|
|
||||||
|
@version 9.3
|
||||||
|
@author Macro People Ltd
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_getpk(
|
||||||
|
lib,
|
||||||
|
ds=0,
|
||||||
|
outds=work.mp_getpk,
|
||||||
|
mdebug=0
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
|
||||||
|
%local engine schema ds1 ds2 ds3 dsn tabs1 tabs2 sum pk4sure pkdefault finalpks;
|
||||||
|
|
||||||
|
%let lib=%upcase(&lib);
|
||||||
|
%let ds=%upcase(&ds);
|
||||||
|
%let engine=%mf_getengine(&lib);
|
||||||
|
%let schema=%mf_getschema(&lib);
|
||||||
|
|
||||||
|
%let ds1=%mf_getuniquename(prefix=getpk_ds1);
|
||||||
|
%let ds2=%mf_getuniquename(prefix=getpk_ds2);
|
||||||
|
%let ds3=%mf_getuniquename(prefix=getpk_ds3);
|
||||||
|
%let tabs1=%mf_getuniquename(prefix=getpk_tabs1);
|
||||||
|
%let tabs2=%mf_getuniquename(prefix=getpk_tabs2);
|
||||||
|
%let sum=%mf_getuniquename(prefix=getpk_sum);
|
||||||
|
%let pk4sure=%mf_getuniquename(prefix=getpk_pk4sure);
|
||||||
|
%let pkdefault=%mf_getuniquename(prefix=getpk_pkdefault);
|
||||||
|
%let finalpks=%mf_getuniquename(prefix=getpk_finalpks);
|
||||||
|
|
||||||
|
%local dbg;
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
%put &sysmacroname entry vars:;
|
||||||
|
%put _local_;
|
||||||
|
%end;
|
||||||
|
%else %let dbg=*;
|
||||||
|
|
||||||
|
proc sql;
|
||||||
|
create table &ds1 as
|
||||||
|
select libname as libref
|
||||||
|
,upcase(memname) as dsn
|
||||||
|
,memtype
|
||||||
|
,upcase(name) as name
|
||||||
|
,type
|
||||||
|
,length
|
||||||
|
,varnum
|
||||||
|
,label
|
||||||
|
,format
|
||||||
|
,idxusage
|
||||||
|
,notnull
|
||||||
|
from dictionary.columns
|
||||||
|
where upcase(libname)="&lib"
|
||||||
|
%if &ds ne 0 %then %do;
|
||||||
|
and upcase(memname)="&ds"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
%if &engine=SQLSVR %then %do;
|
||||||
|
proc sql;
|
||||||
|
connect using &lib;
|
||||||
|
create table work.&ds2 as
|
||||||
|
select * from connection to &lib(
|
||||||
|
select
|
||||||
|
s.name as SchemaName,
|
||||||
|
t.name as memname,
|
||||||
|
tc.name as name,
|
||||||
|
ic.key_ordinal as KeyOrderNr
|
||||||
|
from
|
||||||
|
sys.schemas s
|
||||||
|
inner join sys.tables t on s.schema_id=t.schema_id
|
||||||
|
inner join sys.indexes i on t.object_id=i.object_id
|
||||||
|
inner join sys.index_columns ic on i.object_id=ic.object_id
|
||||||
|
and i.index_id=ic.index_id
|
||||||
|
inner join sys.columns tc on ic.object_id=tc.object_id
|
||||||
|
and ic.column_id=tc.column_id
|
||||||
|
where i.is_primary_key=1
|
||||||
|
and s.name=%str(%')&schema%str(%')
|
||||||
|
order by t.name, ic.key_ordinal ;
|
||||||
|
);disconnect from &lib;
|
||||||
|
create table &ds3 as
|
||||||
|
select a.*
|
||||||
|
,case when b.name is not null then 1 else 0 end as pk_ind
|
||||||
|
from work.&ds1 a
|
||||||
|
left join work.&ds2 b
|
||||||
|
on a.dsn=b.memname
|
||||||
|
and upcase(a.name)=upcase(b.name)
|
||||||
|
order by libref,dsn;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
|
||||||
|
%if &ds = 0 %then %let dsn=;
|
||||||
|
|
||||||
|
/* get all constraints, in constraint order*/
|
||||||
|
%mp_getconstraints(lib=&lib,ds=&dsn,outds=work.&ds2)
|
||||||
|
|
||||||
|
/* extract cols that are clearly primary keys */
|
||||||
|
proc sql;
|
||||||
|
create table &pk4sure as
|
||||||
|
select libref
|
||||||
|
,table_name
|
||||||
|
,constraint_name
|
||||||
|
,constraint_order
|
||||||
|
,column_name as name
|
||||||
|
from work.&ds2
|
||||||
|
where constraint_type='PRIMARY'
|
||||||
|
order by 1,2,3,4;
|
||||||
|
|
||||||
|
/* extract unique constraints where every col is also NOT NULL */
|
||||||
|
proc sql;
|
||||||
|
create table &sum as
|
||||||
|
select a.libref
|
||||||
|
,a.table_name
|
||||||
|
,a.constraint_name
|
||||||
|
,count(a.column_name) as unq_cnt
|
||||||
|
,count(b.column_name) as nul_cnt
|
||||||
|
from work.&ds2(where=(constraint_type ='UNIQUE')) a
|
||||||
|
left join work.&ds2(where=(constraint_type ='NOT NULL')) b
|
||||||
|
on a.libref=b.libref
|
||||||
|
and a.table_name=b.table_name
|
||||||
|
and a.column_name=b.column_name
|
||||||
|
group by 1,2,3
|
||||||
|
having unq_cnt=nul_cnt;
|
||||||
|
|
||||||
|
/* extract cols from the relevant unique constraints */
|
||||||
|
create table &pkdefault as
|
||||||
|
select a.libref
|
||||||
|
,a.table_name
|
||||||
|
,a.constraint_name
|
||||||
|
,b.constraint_order
|
||||||
|
,b.column_name as name
|
||||||
|
from &sum a
|
||||||
|
left join &ds2(where=(constraint_type ='UNIQUE')) b
|
||||||
|
on a.libref=b.libref
|
||||||
|
and a.table_name=b.table_name
|
||||||
|
and a.constraint_name=b.constraint_name
|
||||||
|
order by 1,2,3,4;
|
||||||
|
|
||||||
|
/* create one table */
|
||||||
|
data &finalpks;
|
||||||
|
set &pkdefault &pk4sure ;
|
||||||
|
pk_ind=1;
|
||||||
|
/* if there are multiple unique constraints, take the first */
|
||||||
|
by libref table_name constraint_name;
|
||||||
|
retain keepme;
|
||||||
|
if first.table_name then keepme=1;
|
||||||
|
if first.constraint_name and not first.table_name then keepme=0;
|
||||||
|
if keepme=1;
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* join back to starting table */
|
||||||
|
proc sql;
|
||||||
|
create table &ds3 as
|
||||||
|
select a.*
|
||||||
|
,b.constraint_order
|
||||||
|
,case when b.pk_ind=1 then 1 else 0 end as pk_ind
|
||||||
|
from work.&ds1 a
|
||||||
|
left join work.&finalpks b
|
||||||
|
on a.libref=b.libref
|
||||||
|
and a.dsn=b.table_name
|
||||||
|
and upcase(a.name)=upcase(b.name)
|
||||||
|
order by libref,dsn,constraint_order;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
|
||||||
|
/* prepare tables */
|
||||||
|
proc sql;
|
||||||
|
create table work.&tabs1 as select
|
||||||
|
libname as libref
|
||||||
|
,upcase(memname) as dsn
|
||||||
|
,memtype
|
||||||
|
,dbms_memtype
|
||||||
|
,typemem
|
||||||
|
,memlabel
|
||||||
|
,nvar
|
||||||
|
,compress
|
||||||
|
from dictionary.tables
|
||||||
|
where upcase(libname)="&lib"
|
||||||
|
%if &ds ne 0 %then %do;
|
||||||
|
and upcase(memname)="&ds"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
data &tabs2;
|
||||||
|
set &ds3;
|
||||||
|
length pk_fields $512;
|
||||||
|
retain pk_fields;
|
||||||
|
by libref dsn constraint_order;
|
||||||
|
if first.dsn then pk_fields='';
|
||||||
|
if pk_ind=1 then pk_fields=catx(' ',pk_fields,name);
|
||||||
|
if last.dsn then output;
|
||||||
|
run;
|
||||||
|
|
||||||
|
proc sql;
|
||||||
|
create table &outds as
|
||||||
|
select a.libref
|
||||||
|
,a.dsn
|
||||||
|
,a.memtype
|
||||||
|
,a.dbms_memtype
|
||||||
|
,a.typemem
|
||||||
|
,a.memlabel
|
||||||
|
,a.nvar
|
||||||
|
,a.compress
|
||||||
|
,b.pk_fields
|
||||||
|
from work.&tabs1 a
|
||||||
|
left join work.&tabs2 b
|
||||||
|
on a.libref=b.libref
|
||||||
|
and a.dsn=b.dsn;
|
||||||
|
|
||||||
|
/* tidy up */
|
||||||
|
%mp_dropmembers(
|
||||||
|
&ds1 &ds2 &ds3 &dsn &tabs1 &tabs2 &sum &pk4sure &pkdefault &finalpks,
|
||||||
|
iftrue=(&mdebug=0)
|
||||||
|
)
|
||||||
|
|
||||||
|
%mend mp_getpk;
|
||||||
@@ -1,32 +1,37 @@
|
|||||||
/**
|
/**
|
||||||
@file mp_guesspk.sas
|
@file
|
||||||
@brief Guess the primary key of a table
|
@brief Guess the primary key of a table
|
||||||
@details Tries to guess the primary key of a table based on the following logic:
|
@details Tries to guess the primary key of a table based on the following
|
||||||
|
logic:
|
||||||
|
|
||||||
* Columns with nulls are ignored
|
* Columns with nulls are ignored
|
||||||
* Return only column combinations that provide unique results
|
* Return only column combinations that provide unique results
|
||||||
* Start from one column, then move out to include composite keys of 2 to 6 columns
|
* Start from one column, then move out to composite keys of 2 to 6 columns
|
||||||
|
|
||||||
The library of the target should be assigned before using this macro.
|
The library of the target should be assigned before using this macro.
|
||||||
|
|
||||||
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_guesspk(sashelp.class,outds=classpks)
|
%mp_guesspk(sashelp.class,outds=classpks)
|
||||||
|
|
||||||
@param baseds The dataset to analyse
|
@param baseds The dataset to analyse
|
||||||
@param outds= The output dataset to contain the possible PKs
|
@param outds= The output dataset to contain the possible PKs
|
||||||
@param max_guesses= The total number of possible primary keys to generate. A
|
@param max_guesses= (3) The total number of possible primary keys to generate.
|
||||||
table is likely to have multiple unlikely PKs, so no need to list them all. Default=3.
|
A table may have multiple unlikely PKs, so no need to list them all.
|
||||||
@param min_rows= The minimum number of rows a table should have in order to try
|
@param min_rows= (5) The minimum number of rows a table should have in order
|
||||||
and guess the PK. Default=5.
|
to try and guess the PK.
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getvarlist.sas
|
@li mf_getvarlist.sas
|
||||||
@li mf_getuniquename.sas
|
@li mf_getuniquename.sas
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_getpk.sas
|
||||||
|
|
||||||
@version 9.3
|
@version 9.3
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|
||||||
@@ -196,7 +201,8 @@
|
|||||||
%let lev4=%scan(&posspks,&l);
|
%let lev4=%scan(&posspks,&l);
|
||||||
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then %do;
|
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then %do;
|
||||||
/* check for four level uniqueness */
|
/* check for four level uniqueness */
|
||||||
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4) out=&tmpds noduprec;
|
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4)
|
||||||
|
out=&tmpds noduprec;
|
||||||
by _all_;
|
by _all_;
|
||||||
run;
|
run;
|
||||||
%if %mf_nobs(&tmpds)=&rows %then %do;
|
%if %mf_nobs(&tmpds)=&rows %then %do;
|
||||||
@@ -233,7 +239,8 @@
|
|||||||
%let lev5=%scan(&posspks,&m);
|
%let lev5=%scan(&posspks,&m);
|
||||||
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then %do;
|
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then %do;
|
||||||
/* check for four level uniqueness */
|
/* check for four level uniqueness */
|
||||||
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4 &lev5) out=&tmpds noduprec;
|
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4 &lev5)
|
||||||
|
out=&tmpds noduprec;
|
||||||
by _all_;
|
by _all_;
|
||||||
run;
|
run;
|
||||||
%if %mf_nobs(&tmpds)=&rows %then %do;
|
%if %mf_nobs(&tmpds)=&rows %then %do;
|
||||||
@@ -282,7 +289,8 @@
|
|||||||
run;
|
run;
|
||||||
%if %mf_nobs(&tmpds)=&rows %then %do;
|
%if %mf_nobs(&tmpds)=&rows %then %do;
|
||||||
proc sql;
|
proc sql;
|
||||||
insert into &outds values("&lev1 &lev2 &lev3 &lev4 &lev5 &lev6");
|
insert into &outds
|
||||||
|
values("&lev1 &lev2 &lev3 &lev4 &lev5 &lev6");
|
||||||
%if %mf_nobs(&outds) ge &max_guesses %then %do;
|
%if %mf_nobs(&outds) ge &max_guesses %then %do;
|
||||||
%put &sysmacroname: Max PKs reached at Level 6 for &baseds;
|
%put &sysmacroname: Max PKs reached at Level 6 for &baseds;
|
||||||
%return;
|
%return;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
@param [in] libds dataset to hash
|
@param [in] libds dataset to hash
|
||||||
@param [in] salt= Provide a salt (could be, for instance, the dataset name)
|
@param [in] salt= Provide a salt (could be, for instance, the dataset name)
|
||||||
|
@param [in] iftrue= A condition under which the macro should be executed.
|
||||||
@param [out] outds= (work.mf_hashdataset) The output dataset to create. This
|
@param [out] outds= (work.mf_hashdataset) The output dataset to create. This
|
||||||
will contain one column (hashkey) with one observation (a hex32.
|
will contain one column (hashkey) with one observation (a hex32.
|
||||||
representation of the input hash)
|
representation of the input hash)
|
||||||
@@ -35,10 +36,14 @@
|
|||||||
%macro mp_hashdataset(
|
%macro mp_hashdataset(
|
||||||
libds,
|
libds,
|
||||||
outds=,
|
outds=,
|
||||||
salt=
|
salt=,
|
||||||
|
iftrue=%str(1=1)
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||||
|
|
||||||
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
|
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
|
||||||
%put %str(WARN)ING: Dataset &libds is empty;, or is not a dataset;
|
%put %str(WARN)ING: Dataset &libds is empty, or is not a dataset;
|
||||||
%end;
|
%end;
|
||||||
%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do;
|
%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do;
|
||||||
%put %str(ERR)OR: Dataset &libds is not a dataset;
|
%put %str(ERR)OR: Dataset &libds is not a dataset;
|
||||||
|
|||||||
65
base/mp_init.sas
Normal file
65
base/mp_init.sas
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Initialise session with useful settings and variables
|
||||||
|
@details Implements a "strict" set of SAS options for use in defensive
|
||||||
|
programming. Highly recommended, if you want your code to run on some
|
||||||
|
other machine.
|
||||||
|
|
||||||
|
This macro is recommended to be compiled and invoked in the `initProgram`
|
||||||
|
for SASjs [Jobs](https://cli.sasjs.io/sasjsconfig.html#jobConfig_initProgram
|
||||||
|
), [Services](
|
||||||
|
https://cli.sasjs.io/sasjsconfig.html#serviceConfig_initProgram) and [Tests]
|
||||||
|
(https://cli.sasjs.io/sasjsconfig.html#testConfig_initProgram).
|
||||||
|
|
||||||
|
For non SASjs projects, you could invoke in the autoexec, or in your own
|
||||||
|
solution initialisation macro.
|
||||||
|
|
||||||
|
|
||||||
|
If you have a good idea for another useful option, setting, or global
|
||||||
|
variable - feel free to [raise an issue](
|
||||||
|
https://github.com/sasjs/core/issues/new)!
|
||||||
|
|
||||||
|
All global variables are prefixed with "SASJS" (unless modified with the
|
||||||
|
prefix parameter).
|
||||||
|
|
||||||
|
@param [in] prefix= (SASJS) The prefix to apply to the global macro variables
|
||||||
|
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_init(prefix=
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%global
|
||||||
|
&prefix._INIT_NUM /* initialisation time as numeric */
|
||||||
|
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
|
||||||
|
;
|
||||||
|
%if %eval(&&&prefix._INIT_NUM>0) %then %return; /* only run once */
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
dttm=datetime();
|
||||||
|
call symputx("&prefix._init_num",dttm);
|
||||||
|
call symputx("&prefix._init_dttm",put(dttm,E8601DT26.6));
|
||||||
|
run;
|
||||||
|
|
||||||
|
options
|
||||||
|
autocorrect /* disallow mis-spelled procedure names */
|
||||||
|
compress=CHAR /* default is none so ensure we have something! */
|
||||||
|
datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */
|
||||||
|
%str(err)orcheck=STRICT /* catch errs in libname/filename statements */
|
||||||
|
fmterr /* ensure err when a format cannot be found */
|
||||||
|
mergenoby=%str(ERR)OR /* throw err when a merge has no BY variables */
|
||||||
|
missing=. /* changing this can cause hard to detect errs */
|
||||||
|
noquotelenmax /* avoid warnings for long strings */
|
||||||
|
noreplace /* avoid overwriting permanent datasets */
|
||||||
|
ps=max /* reduce log size slightly */
|
||||||
|
validmemname=COMPATIBLE /* avoid special characters etc in table names */
|
||||||
|
validvarname=V7 /* avoid special characters etc in variable names */
|
||||||
|
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
|
||||||
|
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
|
||||||
|
;
|
||||||
|
|
||||||
|
%mend mp_init;
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
%if &action=OPEN %then %do;
|
%if &action=OPEN %then %do;
|
||||||
options nobomfile;
|
options nobomfile;
|
||||||
data _null_;file &jref encoding='utf-8';
|
data _null_;file &jref encoding='utf-8';
|
||||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
put '{"PROCESSED_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '"';
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
%else %if (&action=ARR or &action=OBJ) %then %do;
|
%else %if (&action=ARR or &action=OBJ) %then %do;
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ run;
|
|||||||
%let abortme=1;
|
%let abortme=1;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
/* catch errors - mp_abort must be called outside of a logic block */
|
/* catch errs - mp_abort must be called outside of a logic block */
|
||||||
%mp_abort(iftrue=(&abortme=1),
|
%mp_abort(iftrue=(&abortme=1),
|
||||||
msg=%superq(msg),
|
msg=%superq(msg),
|
||||||
mac=&sysmacroname
|
mac=&sysmacroname
|
||||||
|
|||||||
87
base/mp_makedata.sas
Normal file
87
base/mp_makedata.sas
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Create sample data based on the structure of an empty table
|
||||||
|
@details Many SAS projects involve sensitive datasets. One way to _ensure_
|
||||||
|
the data is anonymised, is never to receive it in the first place! Often
|
||||||
|
consultants are provided with empty tables, and expected to create complex
|
||||||
|
ETL flows.
|
||||||
|
|
||||||
|
This macro can help by taking an empty table, and populating it with data
|
||||||
|
according to the variable types and formats.
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
@li Respect PKs
|
||||||
|
@li Respect NOT NULLs
|
||||||
|
@li Consider dates, datetimes, times, integers etc
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
proc sql;
|
||||||
|
create table work.example(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
DD_TYPE char(16),
|
||||||
|
DD_SOURCE char(2048),
|
||||||
|
DD_SHORTDESC char(256),
|
||||||
|
constraint pk primary key(tx_from, dd_type,dd_source),
|
||||||
|
constraint nnn not null(DD_SHORTDESC)
|
||||||
|
);
|
||||||
|
%mp_makedata(work.example)
|
||||||
|
|
||||||
|
@param [in] libds The empty table in which to create data
|
||||||
|
@param [out] obs= (500) The number of records to create.
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mf_getvarlen.sas
|
||||||
|
@li mf_nobs.sas
|
||||||
|
@li mp_getcols.sas
|
||||||
|
@li mp_getpk.sas
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_makedata(libds
|
||||||
|
,obs=500
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%local ds1 c1 n1 i col charvars numvars;
|
||||||
|
|
||||||
|
%if %mf_nobs(&libds)>0 %then %do;
|
||||||
|
%put &sysmacroname: &libds has data, it will not be recreated;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%local ds1 c1 n1;
|
||||||
|
%let ds1=%mf_getuniquename(prefix=mp_makedata);
|
||||||
|
%let c1=%mf_getuniquename(prefix=mp_makedatacol);
|
||||||
|
%let n1=%mf_getuniquename(prefix=mp_makedatacol);
|
||||||
|
data &ds1;
|
||||||
|
if 0 then set &libds;
|
||||||
|
do _n_=1 to &obs;
|
||||||
|
&c1=repeat(uuidgen(),10);
|
||||||
|
&n1=ranuni(1)*5000000;
|
||||||
|
drop &c1 &n1;
|
||||||
|
%let charvars=%mf_getvarlist(&libds,typefilter=C);
|
||||||
|
%do i=1 %to %sysfunc(countw(&charvars));
|
||||||
|
%let col=%scan(&charvars,&i);
|
||||||
|
&col=subpad(&c1,1,%mf_getvarlen(&libds,&col));
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%let numvars=%mf_getvarlist(&libds,typefilter=N);
|
||||||
|
%do i=1 %to %sysfunc(countw(&numvars));
|
||||||
|
%let col=%scan(&numvars,&i);
|
||||||
|
&col=&n1;
|
||||||
|
%end;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
proc append base=&libds data=&ds1;
|
||||||
|
run;
|
||||||
|
|
||||||
|
proc sql;
|
||||||
|
drop table &ds1;
|
||||||
|
|
||||||
|
%mend mp_makedata;
|
||||||
27
base/mp_reseterror.sas
Normal file
27
base/mp_reseterror.sas
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Reset when an err condition occurs
|
||||||
|
@details When building apps, sometimes an operation must be attempted that
|
||||||
|
can cause an err condition. There is no try catch in SAS! So the err state
|
||||||
|
must be caught and reset.
|
||||||
|
|
||||||
|
This macro attempts to do that reset.
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_reseterror(
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
options obs=max replace nosyntaxcheck;
|
||||||
|
%let syscc=0;
|
||||||
|
|
||||||
|
%if "&sysprocessmode " = "SAS Stored Process Server " %then %do;
|
||||||
|
data _null_;
|
||||||
|
rc=stpsrvset('program error', 0);
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend mp_reseterror;
|
||||||
@@ -11,22 +11,28 @@
|
|||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%mp_searchdata(lib=sashelp, string=Jan)
|
%mp_searchdata(lib=sashelp, string=Jan)
|
||||||
%mp_searchdata(lib=sashelp, numval=1)
|
%mp_searchdata(lib=sashelp, ds=bird, numval=1)
|
||||||
|
%mp_searchdata(lib=sashelp, ds=class, string=l,outobs=5)
|
||||||
|
|
||||||
|
|
||||||
Outputs zero or more tables to an MPSEARCH library with specific records.
|
Outputs zero or more tables to an MPSEARCH library with specific records.
|
||||||
|
|
||||||
@param lib= the libref to search (should be already assigned)
|
@param [in] lib= The libref to search (should be already assigned)
|
||||||
@param ds= the dataset to search (leave blank to search entire library)
|
@param [in] ds= The dataset to search (leave blank to search entire library)
|
||||||
@param string= the string value to search
|
@param [in] string= String value to search (case sensitive, can be partial)
|
||||||
@param numval= the numeric value to search (must be exact)
|
@param [in] numval= Numeric value to search (must be exact)
|
||||||
@param outloc= the directory in which to create the output datasets with
|
@param [out] outloc= (0) Optionally specify the directory in which to
|
||||||
matching rows. Will default to a subfolder in the WORK library.
|
create the the output datasets with matching rows. By default it will
|
||||||
@param outobs= set to a positive integer to restrict the number of
|
write them to a temporary subdirectory within the WORK folder.
|
||||||
|
@param [out] outlib= (MPSEARCH) Assign a different libref to the output
|
||||||
|
library containing the matching datasets / records
|
||||||
|
@param [in] outobs= set to a positive integer to restrict the number of
|
||||||
observations
|
observations
|
||||||
@param filter_text= add a (valid) filter clause to further filter the results
|
@param [in] filter_text= (1=1) Add a (valid) filter clause to further filter
|
||||||
|
the results.
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquename.sas
|
||||||
@li mf_getvarlist.sas
|
@li mf_getvarlist.sas
|
||||||
@li mf_getvartype.sas
|
@li mf_getvartype.sas
|
||||||
@li mf_mkdir.sas
|
@li mf_mkdir.sas
|
||||||
@@ -36,11 +42,12 @@
|
|||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mp_searchdata(lib=sashelp
|
%macro mp_searchdata(lib=
|
||||||
,ds=
|
,ds=
|
||||||
,string= /* the query will use a contains (?) operator */
|
,string= /* the query will use a contains (?) operator */
|
||||||
,numval= /* numeric must match exactly */
|
,numval= /* numeric must match exactly */
|
||||||
,outloc=%sysfunc(pathname(work))/mpsearch
|
,outloc=0
|
||||||
|
,outlib=MPSEARCH
|
||||||
,outobs=-1
|
,outobs=-1
|
||||||
,filter_text=%str(1=1)
|
,filter_text=%str(1=1)
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
@@ -57,8 +64,12 @@
|
|||||||
%if &string = %then %let type=N;
|
%if &string = %then %let type=N;
|
||||||
%else %let type=C;
|
%else %let type=C;
|
||||||
|
|
||||||
|
%if "&outloc"="0" %then %do;
|
||||||
|
%let outloc=%sysfunc(pathname(work))/%mf_getuniquename();
|
||||||
|
%end;
|
||||||
|
|
||||||
%mf_mkdir(&outloc)
|
%mf_mkdir(&outloc)
|
||||||
libname mpsearch "&outloc";
|
libname &outlib "&outloc";
|
||||||
|
|
||||||
/* get the list of tables in the library */
|
/* get the list of tables in the library */
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
@@ -70,11 +81,6 @@ select distinct memname into: table_list separated by ' '
|
|||||||
%end;
|
%end;
|
||||||
;
|
;
|
||||||
/* check that we have something to check */
|
/* check that we have something to check */
|
||||||
proc sql
|
|
||||||
%if &outobs>-1 %then %do;
|
|
||||||
outobs=&outobs
|
|
||||||
%end;
|
|
||||||
;
|
|
||||||
%if %length(&table_list)=0 %then %put library &lib contains no tables!;
|
%if %length(&table_list)=0 %then %put library &lib contains no tables!;
|
||||||
/* loop through each table */
|
/* loop through each table */
|
||||||
%else %do table_num=1 %to %sysfunc(countw(&table_list,%str( )));
|
%else %do table_num=1 %to %sysfunc(countw(&table_list,%str( )));
|
||||||
@@ -85,10 +91,10 @@ proc sql
|
|||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
%let check_tm=%sysfunc(datetime());
|
%let check_tm=%sysfunc(datetime());
|
||||||
/* build sql statement */
|
/* prep input */
|
||||||
create table mpsearch.&table as select * from &lib..&table
|
data &outlib..&table;
|
||||||
where %unquote(&filter_text) and
|
set &lib..&table;
|
||||||
(0
|
where %unquote(&filter_text) and ( 0
|
||||||
/* loop through columns */
|
/* loop through columns */
|
||||||
%do colnum=1 %to %sysfunc(countw(&vars,%str( )));
|
%do colnum=1 %to %sysfunc(countw(&vars,%str( )));
|
||||||
%let col=%scan(&vars,&colnum,%str( ));
|
%let col=%scan(&vars,&colnum,%str( ));
|
||||||
@@ -102,15 +108,20 @@ proc sql
|
|||||||
or ("&col"n = &numval)
|
or ("&col"n = &numval)
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
);
|
);
|
||||||
|
%if &outobs>-1 %then %do;
|
||||||
|
if _n_ > &outobs then stop;
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
%put Search query for &table took
|
%put Search query for &table took
|
||||||
%sysevalf(%sysfunc(datetime())-&check_tm) seconds;
|
%sysevalf(%sysfunc(datetime())-&check_tm) seconds;
|
||||||
%if &sqlrc ne 0 %then %do;
|
%if &syscc ne 0 %then %do;
|
||||||
%put %str(WAR)NING: SQLRC=&sqlrc when processing &table;
|
%put %str(ERR)ROR: SYSCC=&syscc when processing &lib..&table;
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
%if %mf_nobs(mpsearch.&table)=0 %then %do;
|
%if %mf_nobs(&outlib..&table)=0 %then %do;
|
||||||
drop table mpsearch.&table;
|
proc sql;
|
||||||
|
drop table &outlib..&table;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -9,11 +9,14 @@
|
|||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_existds.sas
|
@li mf_existds.sas
|
||||||
|
|
||||||
@param key Provide a key on which to perform the lookup
|
<h4> Related Macros </h4>
|
||||||
@param value Provide a value
|
@li mf_getvalue.sas
|
||||||
@param type= either C or N will populate valc and valn respectively. C is
|
|
||||||
default.
|
@param [in] key Provide a key on which to perform the lookup
|
||||||
@param libds= define the target table to hold the parameters
|
@param [in] value Provide a value
|
||||||
|
@param [in] type= either C or N will populate valc and valn respectively.
|
||||||
|
C is default.
|
||||||
|
@param [out] libds= define the target table to hold the parameters
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|||||||
118
base/mp_sortinplace.sas
Normal file
118
base/mp_sortinplace.sas
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Sorts a SAS dataset in place, preserving constraints
|
||||||
|
@details Generally if a dataset contains indexes, then it is not necessary to
|
||||||
|
sort it before performing operations such as merges / joins etc.
|
||||||
|
That said, there are a few edge cases where it can be desirable:
|
||||||
|
|
||||||
|
@li To improve performance for particular scenarios
|
||||||
|
@li To allow adjacent records to be viewed directly in the dataset
|
||||||
|
@li To reduce dataset size (eg when there are deleted records)
|
||||||
|
|
||||||
|
This macro will only work for BASE (V9) engine libraries. It works by
|
||||||
|
creating a copy of the dataset (without data, WITH constraints) in the same
|
||||||
|
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>
|
||||||
|
@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
|
||||||
|
@author Allan Bowe
|
||||||
|
@source https://github.com/sasjs/core
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_sortinplace(libds
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%local lib ds tempds1 tempds2 tempvw sortkey;
|
||||||
|
|
||||||
|
/* perform validations */
|
||||||
|
%mp_abort(iftrue=(%sysfunc(countc(&libds,.)) ne 1)
|
||||||
|
,mac=mp_sortinplace
|
||||||
|
,msg=%str(LIBDS (&libds) should have LIBREF.DATASET format)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue=(%mf_existds(&libds)=0)
|
||||||
|
,mac=mp_sortinplace
|
||||||
|
,msg=%str(&libds does not exist)
|
||||||
|
)
|
||||||
|
|
||||||
|
%let lib=%scan(&libds,1,.);
|
||||||
|
%let ds=%scan(&libds,2,.);
|
||||||
|
%mp_abort(iftrue=(%mf_getengine(&lib) ne V9)
|
||||||
|
,mac=mp_sortinplace
|
||||||
|
,msg=%str(&lib is not a BASE engine library)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* grab a copy of the constraints so we know what to sort by */
|
||||||
|
%let tempds1=%mf_getuniquename(prefix=&sysmacroname);
|
||||||
|
%mp_getpk(lib=&lib,ds=&ds,outds=work.&tempds1)
|
||||||
|
|
||||||
|
%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);
|
||||||
|
proc append base=&lib..&tempds2 data=&libds(obs=0);
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* create sorted view */
|
||||||
|
%let tempvw=%mf_getuniquename(prefix=&sysmacroname);
|
||||||
|
proc sql;
|
||||||
|
create view work.&tempvw as select * from &lib..&ds
|
||||||
|
order by %mf_getquotedstr(&sortkey,quote=N);
|
||||||
|
|
||||||
|
/* 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;
|
||||||
@@ -7,19 +7,23 @@
|
|||||||
|
|
||||||
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
|
||||||
@@ -31,18 +35,20 @@
|
|||||||
,outdir=%sysfunc(pathname(work))
|
,outdir=%sysfunc(pathname(work))
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%local fname1 fname2 fname3;
|
%local f1 f2 ;
|
||||||
%let fname1=%mf_getuniquefileref();
|
%let f1=%mf_getuniquefileref();
|
||||||
%let fname2=%mf_getuniquefileref();
|
%let f2=%mf_getuniquefileref();
|
||||||
%let fname3=%mf_getuniquefileref();
|
|
||||||
|
|
||||||
/* Macro variable &datazip would be read from the file */
|
/* Macro variable &datazip would be read from the file */
|
||||||
filename &fname1 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;
|
||||||
fid=dopen("&fname1");
|
fid=dopen("&f1");
|
||||||
if fid=0 then stop;
|
if fid=0 then stop;
|
||||||
memcount=dnum(fid);
|
memcount=dnum(fid);
|
||||||
do i=1 to memcount;
|
do i=1 to memcount;
|
||||||
@@ -53,16 +59,32 @@ data _data_(keep=memname isFolder);
|
|||||||
end;
|
end;
|
||||||
rc=dclose(fid);
|
rc=dclose(fid);
|
||||||
run;
|
run;
|
||||||
filename &fname1 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 */
|
||||||
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 call execute('filename &fname2 zip &ziploc member='
|
else do;
|
||||||
!!quote(trim(memname))!!';filename &fname3 "&outdir/'
|
qname=quote(cats("&outdir/",memname));
|
||||||
!!trim(memname)!!'" recfm=n;data _null_; rc=fcopy("&fname2","&fname3");run;'
|
bname=cats('(',memname,')');
|
||||||
!!'filename &fname2 clear; filename &fname3 clear;');
|
put '/* hat tip: "data _null_" on SAS-L */';
|
||||||
|
put 'data _null_;';
|
||||||
|
put ' infile &f1 ' bname ' lrecl=256 recfm=F length=length eof=eof unbuf;';
|
||||||
|
put ' file ' qname ' lrecl=256 recfm=N;';
|
||||||
|
put ' input;';
|
||||||
|
put ' put _infile_ $varying256. length;';
|
||||||
|
put ' return;';
|
||||||
|
put 'eof:';
|
||||||
|
put ' stop;';
|
||||||
|
put 'run;';
|
||||||
|
end;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%mend mp_unzip;
|
%inc &f2/source2;
|
||||||
|
|
||||||
|
filename &f2 clear;
|
||||||
|
|
||||||
|
%mend mp_unzip;
|
||||||
|
|||||||
@@ -54,6 +54,9 @@
|
|||||||
@param [out] pkg= (utils) The output package in which to create the function.
|
@param [out] pkg= (utils) The output package in which to create the function.
|
||||||
Uses a 3 part format: libref.catalog.package
|
Uses a 3 part format: libref.catalog.package
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_existfunction.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mcf_stpsrv_header(wrap=NO
|
%macro mcf_stpsrv_header(wrap=NO
|
||||||
@@ -63,6 +66,8 @@
|
|||||||
,pkg=UTILS
|
,pkg=UTILS
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%if %mf_existfunction(stpsrv_header)=1 %then %return;
|
||||||
|
|
||||||
%if &wrap=YES %then %do;
|
%if &wrap=YES %then %do;
|
||||||
proc fcmp outcat=&lib..&cat..&pkg;
|
proc fcmp outcat=&lib..&cat..&pkg;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ data _null_;
|
|||||||
put '%if &action=OPEN %then %do; ';
|
put '%if &action=OPEN %then %do; ';
|
||||||
put ' options nobomfile; ';
|
put ' options nobomfile; ';
|
||||||
put ' data _null_;file &jref encoding=''utf-8''; ';
|
put ' data _null_;file &jref encoding=''utf-8''; ';
|
||||||
put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; ';
|
put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put '%end; ';
|
put '%end; ';
|
||||||
put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
|
put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
|
||||||
|
|||||||
1430
package-lock.json
generated
1430
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -34,5 +34,8 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sasjs/cli": "^2.39.0"
|
"@sasjs/cli": "^2.39.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ts-loader": "^9.2.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,13 +48,20 @@
|
|||||||
"name": "sas9",
|
"name": "sas9",
|
||||||
"serverUrl": "https://sas.analytium.co.uk:8343",
|
"serverUrl": "https://sas.analytium.co.uk:8343",
|
||||||
"serverType": "SAS9",
|
"serverType": "SAS9",
|
||||||
|
"httpsAgentOptions": {
|
||||||
|
"allowInsecureRequests": false
|
||||||
|
},
|
||||||
"appLoc": "/Shared Data/temp/macrocore",
|
"appLoc": "/Shared Data/temp/macrocore",
|
||||||
"macroFolders": [
|
"macroFolders": [
|
||||||
"tests/sas9only"
|
"tests/sas9only"
|
||||||
],
|
],
|
||||||
|
"programFolders": [],
|
||||||
"deployConfig": {
|
"deployConfig": {
|
||||||
"deployServicePack": true
|
"deployServicePack": true,
|
||||||
}
|
"deployScripts": []
|
||||||
|
},
|
||||||
|
"serverName": "SASApp",
|
||||||
|
"repositoryName": "Foundation"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
|
|
||||||
%else %if &action=ARR or &action=OBJ %then %do;
|
%else %if &action=ARR or &action=OBJ %then %do;
|
||||||
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
|
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
|
||||||
,engine=&jsonengine,dbg=%str(&_debug)
|
,engine=DATASTEP,dbg=%str(&_debug)
|
||||||
)
|
)
|
||||||
%end;
|
%end;
|
||||||
%else %if &action=CLOSE %then %do;
|
%else %if &action=CLOSE %then %do;
|
||||||
@@ -124,8 +124,8 @@
|
|||||||
put " ""&wt"" : {";
|
put " ""&wt"" : {";
|
||||||
put '"nlobs":' nlobs;
|
put '"nlobs":' nlobs;
|
||||||
put ',"nvars":' nvars;
|
put ',"nvars":' nvars;
|
||||||
%mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=&jsonengine)
|
%mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP)
|
||||||
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=&jsonengine)
|
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP)
|
||||||
data _null_; file &fref mod encoding='utf-8';
|
data _null_; file &fref mod encoding='utf-8';
|
||||||
put "}";
|
put "}";
|
||||||
%end;
|
%end;
|
||||||
@@ -159,6 +159,7 @@
|
|||||||
autoexec=quote(trim(getoption('autoexec')));
|
autoexec=quote(trim(getoption('autoexec')));
|
||||||
put ',"AUTOEXEC" : ' autoexec;
|
put ',"AUTOEXEC" : ' autoexec;
|
||||||
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
|
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
|
||||||
|
memsize=quote(cats(memsize));
|
||||||
put ',"MEMSIZE" : ' memsize;
|
put ',"MEMSIZE" : ' memsize;
|
||||||
put "}" @;
|
put "}" @;
|
||||||
%if %str(&_debug) ge 131 %then %do;
|
%if %str(&_debug) ge 131 %then %do;
|
||||||
|
|||||||
25
tests/crossplatform/mf_existds.test.sas
Normal file
25
tests/crossplatform/mf_existds.test.sas
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mf_existfileref macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_existds.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
data work.testme;
|
||||||
|
x=1;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_existds(work.testme)=1),
|
||||||
|
desc=Checking existing dataset exists,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_existds(work.try2testme)=0),
|
||||||
|
desc=Checking non existing dataset does not exist,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
@@ -15,10 +15,12 @@ filename inc temp;
|
|||||||
data _null_;
|
data _null_;
|
||||||
set work.test;
|
set work.test;
|
||||||
file inc;
|
file inc;
|
||||||
line=cats('%mp_ds2fmtds(sashelp.',memname,',',memname,')');
|
libds=cats('sashelp.',memname);
|
||||||
|
if exist(libds) then line=cats('%mp_ds2fmtds(',libds,',',memname,')');
|
||||||
put line;
|
put line;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
options obs=50;
|
||||||
%inc inc;
|
%inc inc;
|
||||||
|
|
||||||
%mp_assert(
|
%mp_assert(
|
||||||
|
|||||||
88
tests/crossplatform/mp_getpk.test.sas
Normal file
88
tests/crossplatform/mp_getpk.test.sas
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_getpk.sas macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_nobs.sas
|
||||||
|
@li mp_getpk.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
@li mp_assertdsobs.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_getpk.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
/* ensure PK arrives in corrrect order */
|
||||||
|
proc sql;
|
||||||
|
create table work.example1(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
DD_TYPE char(16),
|
||||||
|
DD_SOURCE char(2048),
|
||||||
|
DD_SHORTDESC char(256),
|
||||||
|
constraint pk primary key(tx_from, dd_type,dd_source),
|
||||||
|
constraint unq unique(tx_from, dd_type),
|
||||||
|
constraint nnn not null(DD_SHORTDESC)
|
||||||
|
);
|
||||||
|
%mp_getpk(work,ds=example1,outds=test1)
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
set work.test1;
|
||||||
|
call symputx('test1',pk_fields);
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&test1"="TX_FROM DD_TYPE DD_SOURCE"),
|
||||||
|
desc=mp_getpk gets regular PK values in correct order,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* unique key with NOT NULL captured */
|
||||||
|
proc sql;
|
||||||
|
create table work.example2(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
DD_TYPE char(16),
|
||||||
|
DD_SOURCE char(2048),
|
||||||
|
DD_SHORTDESC char(256),
|
||||||
|
constraint unq1 unique(tx_from, dd_type),
|
||||||
|
constraint unq2 unique(tx_from, dd_type, dd_source),
|
||||||
|
constraint nnn not null(tx_from),
|
||||||
|
constraint nnnn not null(dd_type)
|
||||||
|
);
|
||||||
|
%mp_getpk(work,ds=example2,outds=test2)
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
set work.test1;
|
||||||
|
call symputx('test2',pk_fields);
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&test2"="TX_FROM DD_TYPE"),
|
||||||
|
desc=mp_getpk gets unique constraint with NOT NULL in correct order,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* unique key without NOT NULL NOT captured */
|
||||||
|
proc sql;
|
||||||
|
create table work.example3(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
DD_TYPE char(16),
|
||||||
|
DD_SOURCE char(2048),
|
||||||
|
DD_SHORTDESC char(256),
|
||||||
|
constraint unq1 unique(tx_from, dd_type),
|
||||||
|
constraint unq2 unique(tx_from, dd_type, dd_source),
|
||||||
|
constraint nnn not null(tx_from),
|
||||||
|
constraint nnnn not null(dd_type)
|
||||||
|
);
|
||||||
|
%mp_getpk(work,ds=example3,outds=test3)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_nobs(work.test3)=0),
|
||||||
|
desc=mp_getpk does not capture unique constraint without NOT NULL,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* constraint capture at library level is functional - uses first 2 tests */
|
||||||
|
%mp_getpk(work,outds=test4)
|
||||||
|
|
||||||
|
%mp_assertdsobs(work.test4,test=ATLEAST 2)
|
||||||
24
tests/crossplatform/mp_init.test.sas
Normal file
24
tests/crossplatform/mp_init.test.sas
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_gsubfile.sas macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_init.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test 1 - mp_init.sas actually already ran as part of testinit
|
||||||
|
* So lets test to make sure it will not run again
|
||||||
|
*/
|
||||||
|
|
||||||
|
%let initial_value=&sasjs_init_num;
|
||||||
|
|
||||||
|
%mp_init();
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&initial_value"="&sasjs_init_num"),
|
||||||
|
desc=Check that mp_init() did not run twice,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
@@ -11,9 +11,10 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
/* grab 20 datasets from SASHELP */
|
/* grab 20 datasets from SASHELP */
|
||||||
%let path=%sysfunc(pathname(work));
|
%let work=%sysfunc(pathname(work));
|
||||||
|
%let path=&work/new;
|
||||||
%mf_mkdir(&path)
|
%mf_mkdir(&path)
|
||||||
libname sashlp "&path";
|
libname sashlp "&work";
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
create table members as
|
create table members as
|
||||||
select distinct lowcase(memname) as memname
|
select distinct lowcase(memname) as memname
|
||||||
@@ -31,6 +32,7 @@ run;
|
|||||||
%mp_lib2inserts(sashlp, schema=work, outref=tempref,maxobs=50)
|
%mp_lib2inserts(sashlp, schema=work, outref=tempref,maxobs=50)
|
||||||
|
|
||||||
/* check if it actually runs */
|
/* check if it actually runs */
|
||||||
|
libname sashlp "&path";
|
||||||
options source2;
|
options source2;
|
||||||
%inc tempref;
|
%inc tempref;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_lockfilecheck.sas
|
@li mp_lockfilecheck.sas
|
||||||
@li mp_assert.sas
|
@li mp_assert.sas
|
||||||
|
@li mp_reseterror.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
@@ -29,6 +30,8 @@ data work.test; a=1;run;
|
|||||||
|
|
||||||
%mp_lockfilecheck(sashelp.class)
|
%mp_lockfilecheck(sashelp.class)
|
||||||
|
|
||||||
|
%mp_reseterror()
|
||||||
|
|
||||||
%mp_assert(
|
%mp_assert(
|
||||||
iftrue=(&success=1),
|
iftrue=(&success=1),
|
||||||
desc=Checking sashelp table cannot be locked,
|
desc=Checking sashelp table cannot be locked,
|
||||||
|
|||||||
23
tests/crossplatform/mp_reseterror.test.sas
Normal file
23
tests/crossplatform/mp_reseterror.test.sas
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_reseterror macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_assert.sas
|
||||||
|
@li mp_reseterror.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
/* cause an error */
|
||||||
|
|
||||||
|
lock sashelp.class;
|
||||||
|
|
||||||
|
/* recover ? */
|
||||||
|
%mp_reseterror()
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&syscc=0),
|
||||||
|
desc=Checking error condition was fixed,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
29
tests/crossplatform/mp_searchdata.test.sas
Normal file
29
tests/crossplatform/mp_searchdata.test.sas
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_searchdata.sas
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_searchdata.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
/** Test 1 - generic useage */
|
||||||
|
|
||||||
|
%mp_searchdata(lib=sashelp, ds=class, string=a)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&syscc=0),
|
||||||
|
desc=No errors in regular usage,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Test 2 - with obs issue */
|
||||||
|
|
||||||
|
%mp_searchdata(lib=sashelp, ds=class, string=l,outobs=5)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&SYSWARNINGTEXT" = ""),
|
||||||
|
desc=Ensuring WARN status is clean,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
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
|
||||||
|
)
|
||||||
@@ -2,11 +2,17 @@
|
|||||||
@file
|
@file
|
||||||
@brief init file for tests
|
@brief init file for tests
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_init.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
/* location in metadata or SAS Drive for temporary files */
|
/* location in metadata or SAS Drive for temporary files */
|
||||||
%let mcTestAppLoc=/Public/temp/macrocore;
|
%let mcTestAppLoc=/Public/temp/macrocore;
|
||||||
|
|
||||||
|
/* set defaults */
|
||||||
|
%mp_init()
|
||||||
|
|
||||||
%macro loglevel();
|
%macro loglevel();
|
||||||
%if &_debug=2477 %then %do;
|
%if &_debug=2477 %then %do;
|
||||||
options mprint;
|
options mprint;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ run;
|
|||||||
%put TEST1: checking web service code;
|
%put TEST1: checking web service code;
|
||||||
data work.test_results;
|
data work.test_results;
|
||||||
length test_description $256 test_result $4 test_comments $256;
|
length test_description $256 test_result $4 test_comments $256;
|
||||||
|
if _n_=1 then call missing (of _all_);
|
||||||
infile compare end=eof;
|
infile compare end=eof;
|
||||||
input;
|
input;
|
||||||
if eof then do;
|
if eof then do;
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ data _null_;
|
|||||||
put '%if &action=OPEN %then %do; ';
|
put '%if &action=OPEN %then %do; ';
|
||||||
put ' options nobomfile; ';
|
put ' options nobomfile; ';
|
||||||
put ' data _null_;file &jref encoding=''utf-8''; ';
|
put ' data _null_;file &jref encoding=''utf-8''; ';
|
||||||
put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; ';
|
put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put '%end; ';
|
put '%end; ';
|
||||||
put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
|
put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ options noquotelenmax;
|
|||||||
libname &libref2 JSON fileref=&fname2;
|
libname &libref2 JSON fileref=&fname2;
|
||||||
data &outds;
|
data &outds;
|
||||||
length id $36 name $128 uri $64 type $32 description $256;
|
length id $36 name $128 uri $64 type $32 description $256;
|
||||||
|
if _n_=1 then call missing (of _all_);
|
||||||
set &libref2..items;
|
set &libref2..items;
|
||||||
run;
|
run;
|
||||||
filename &fname2 clear;
|
filename &fname2 clear;
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ run;
|
|||||||
data &outds;
|
data &outds;
|
||||||
format _program uri $128. state $32. stateDetails $32. timestamp datetime19.
|
format _program uri $128. state $32. stateDetails $32. timestamp datetime19.
|
||||||
jobparams $32767.;
|
jobparams $32767.;
|
||||||
|
call missing (of _all_);
|
||||||
stop;
|
stop;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user