1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-24 03:31:19 +00:00

Compare commits

...

10 Commits

7 changed files with 365 additions and 89 deletions

225
all.sas
View File

@@ -380,7 +380,7 @@ options noquotelenmax;
@brief Returns the engine type of a SAS library
@details Usage:
%put %mf_getEngine(SASHELP);
%put %mf_getengine(SASHELP);
returns:
> V9
@@ -398,9 +398,10 @@ options noquotelenmax;
@version 9.2
@author Allan Bowe
**/
%macro mf_getEngine(libref
**/ /** \cond */
%macro mf_getengine(libref
)/*/STORE SOURCE*/;
%local dsid engnum rc engine;
@@ -419,7 +420,8 @@ options noquotelenmax;
&engine
%mend;
/**
/** \endcond *//**
@file
@brief Returns the size of a file in bytes.
@details Provide full path/filename.extension to the file, eg:
@@ -1902,6 +1904,126 @@ Usage:
%mend;
/**
@file mp_csv2ds.sas
@brief Efficient import of arbitrary CSV using a dataset as template
@details Used to import relevant columns from a large CSV using
a dataset to provide the types and lengths. Assumes that a header
row is provided, and datarows start on line 2. Extra columns in
both the CSV and base dataset are ignored.
Usage:
filename mycsv temp;
data _null_;
file mycsv;
put 'name,age,nickname';
put 'John,48,Jonny';
put 'Jennifer,23,Jen';
run;
%mp_csv2ds(inref=mycsv,outds=myds,baseds=sashelp.class)
@param inref= fileref to the CSV
@param outds= output ds (lib.ds format)
@param view= Set to YES or NO to determine whether the output should be
a view or not. Default is NO (not a view).
@param baseds= Template dataset on which to create the input statement.
Is used to determine types, lengths, and any informats.
@version 9.2
@author Allan Bowe
**/
%macro mp_csv2ds(inref=0,outds=0,baseds=0,view=NO);
%if &inref=0 %then %do;
%put %str(ERR)OR: the INREF variable must be provided;
%let syscc=4;
%abort;
%end;
%if %superq(outds)=0 %then %do;
%put %str(ERR)OR: the OUTDS variable must be provided;
%let syscc=4;
%return;
%end;
%if &baseds=0 %then %do;
%put %str(ERR)OR: the BASEDS variable must be provided;
%let syscc=4;
%return;
%end;
%if %sysfunc(exist(&BASEDS)) ne 1 & %sysfunc(exist(&BASEDS,VIEW)) ne 1 %then %do;
%put %str(ERR)OR: the BASEDS dataset (&baseds) needs to be assigned, and to exist;
%let syscc=4;
%return;
%end;
/* get the variables in the CSV */
data _data_;
infile &inref;
input;
length name $32;
do i=1 to countc(_infile_,',')+1;
name=upcase(scan(_infile_,i,','));
output;
end;
stop;
run;
%local csv_vars;%let csv_vars=&syslast;
/* get the variables in the dataset */
proc contents noprint data=&baseds
out=_data_ (keep=name type length format: informat);
run;
%local base_vars; %let base_vars=&syslast;
proc sql undo_policy=none;
create table &csv_vars as
select a.*
,b.type
,b.length
,b.format
,b.formatd
,b.formatl
,b.informat
from &csv_vars a
left join &base_vars b
on a.name=upcase(b.name)
order by i;
/* prepare the input statement */
%local instat dropvars;
data _null_;
set &syslast end=last;
length in dropvars $32767;
retain in dropvars;
if missing(type) then do;
informat='$1.';
dropvars=catx(' ',dropvars,name);
end;
else if missing(informat) then do;
if type=1 then informat='best.';
else informat=cats('$',length,'.');
end;
else informat=cats(informat,'.');
in=catx(' ',in,name,':',informat);
if last then do;
call symputx('instat',in,'l');
call symputx('dropvars',dropvars,'l');
end;
run;
/* import the CSV */
data &outds
%if %upcase(&view)=YES %then %do;
/view=&outds
%end;
;
infile &inref dsd firstobs=2;
input &instat;
drop &dropvars;
run;
%mend;/**
@file mp_deleteconstraints.sas
@brief Delete constraionts
@details Takes the output from mp_getconstraints.sas as input
@@ -1955,17 +2077,17 @@ run;
%mend;/**
@file
@brief Returns all files and subdirectories within a specified parent
@details When used with getattrs=NO, is not OS specific (uses dopen / dread).
@details When used with getattrs=NO, is not OS specific (uses dopen / dread).
If getattrs=YES then the doptname / foptname functions are used to scan all
properties - any characters that are not valid in a SAS name (v7) are simply
properties - any characters that are not valid in a SAS name (v7) are simply
stripped, and the table is transposed so theat each property is a column
and there is one file per row. An attempt is made to get all properties
and there is one file per row. An attempt is made to get all properties
whether a file or folder, but some files/folders cannot be accessed, and so
not all properties can / will be populated.
Credit for the rename approach:
https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
usage:
@@ -1974,21 +2096,25 @@ run;
%mp_dirlist(outds=cwdfileprops, getattrs=YES)
@warning In a Unix environment, the existence of a named pipe will cause this
%mp_dirlist(fref=MYFREF)
@warning In a Unix environment, the existence of a named pipe will cause this
macro to hang. Therefore this tool should be used with caution in a SAS 9 web
application, as it can use up all available multibridge sessions if requests
are resubmitted.
If anyone finds a way to positively identify a named pipe using SAS (without
If anyone finds a way to positively identify a named pipe using SAS (without
X CMD) do please raise an issue!
@param path= for which to return contents
@param fref= Provide a DISK engine fileref as an alternative to PATH
@param outds= the output dataset to create
@param getattrs= YES/NO (default=NO). Uses doptname and foptname to return
all attributes for each file / folder.
@param getattrs= YES/NO (default=NO). Uses doptname and foptname to return
all attributes for each file / folder.
@returns outds contains the following variables:
- directory (containing folder)
- file_or_folder (file / folder)
- filepath (path/to/file.name)
- filename (just the file name)
@@ -2001,18 +2127,26 @@ run;
**/
%macro mp_dirlist(path=%sysfunc(pathname(work))
, fref=0
, outds=work.mp_dirlist
, getattrs=NO
)/*/STORE SOURCE*/;
%let getattrs=%upcase(&getattrs)XX;
data &outds (compress=no keep=file_or_folder filepath filename ext msg);
length filepath $500 fref fref2 $8 file_or_folder $6 filename $80 ext $20 msg $200;
rc = filename(fref, "&path");
data &outds (compress=no keep=file_or_folder filepath filename ext msg directory);
length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80 ext $20 msg $200;
%if &fref=0 %then %do;
rc = filename(fref, "&path");
%end;
%else %do;
fref="&fref";
rc=0;
%end;
if rc = 0 then do;
did = dopen(fref);
directory=dinfo(did,'Directory');
if did=0 then do;
putlog "NOTE: This directory is empty - &path";
putlog "NOTE: This directory is empty - " directory;
msg=sysmsg();
put _all_;
stop;
@@ -2027,7 +2161,8 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg);
dnum = dnum(did);
do i = 1 to dnum;
filename = dread(did, i);
rc = filename(fref2, "&path/"!!filename);
filepath=cats(directory,'/',filename);
rc = filename(fref2,filepath);
midd=dopen(fref2);
dmsg=sysmsg();
if did > 0 then file_or_folder='folder';
@@ -2036,12 +2171,12 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg);
fmsg=sysmsg();
if midf > 0 then file_or_folder='file';
rc=fclose(midf);
if index(fmsg,'File is in use') or index(dmsg,'is not a directory')
if index(fmsg,'File is in use') or index(dmsg,'is not a directory')
then file_or_folder='file';
else if index(fmsg, 'Insufficient authorization') then file_or_folder='file';
else if file_or_folder='' then file_or_folder='locked';
if file_or_folder='file' then do;
ext = prxchange('s/.*\.{1,1}(.*)/$1/', 1, filename);
if filename = ext then ext = ' ';
@@ -2050,7 +2185,6 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg);
ext='';
file_or_folder='folder';
end;
filepath="&path/"!!filename;
output;
end;
rc = dclose(did);
@@ -2074,7 +2208,7 @@ run;
else do i=1 to foptnum(fid);
infoname=foptname(fid,i);
infoval=finfo(fid,infoname);
sasname=compress(infoname, '_', 'adik');
sasname=compress(infoname, '_', 'adik');
if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
if upcase(sasname) ne 'FILENAME' then output;
end;
@@ -2091,7 +2225,7 @@ run;
else do i=1 to doptnum(fid);
infoname=doptname(fid,i);
infoval=dinfo(fid,infoname);
sasname=compress(infoname, '_', 'adik');
sasname=compress(infoname, '_', 'adik');
if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
if upcase(sasname) ne 'FILENAME' then output;
end;
@@ -2527,7 +2661,6 @@ create table &outds as
datetime2 format or regular decimal type
@version 9.3
@author Allan Bowe
@source https://github.com/sasjs/core
**/
%macro mp_getddl(libref,ds,fref=getddl,flavour=SAS,showlog=NO,schema=
@@ -2629,7 +2762,7 @@ run;
%let curds=%scan(&dsnlist,&x);
data _null_;
file &fref mod;
length nm lab $1024;
length nm lab $1024 typ $20;
set &colinfo (where=(upcase(memname)="&curds")) end=last;
if _n_=1 then do;
@@ -2643,10 +2776,12 @@ run;
end;
else put " ,"@@;
if length(format)>1 then fmt=" format="!!cats(format);
len=" length="!!cats(length);
lab=" label="!!quote(trim(label));
if length(label)>1 then lab=" label="!!quote(trim(label));
if notnull='yes' then notnul=' not null';
put name type len fmt notnul lab;
if type='char' then typ=cats('char(',length,')');
else if length ne 8 then typ='num length='!!left(length);
else typ='num';
put name typ fmt notnul lab;
run;
/* Extra step for data constraints */
@@ -2656,7 +2791,7 @@ run;
file &fref mod;
put ');';
run;
/* Create Unique Indexes, but only if they were not already defined within the Constraints section. */
data _null_;
*length ds $128;
@@ -2836,7 +2971,7 @@ run;
%end;
%end;
%if &showlog=YES %then %do;
%if %upcase(&showlog)=YES %then %do;
options ps=max;
data _null_;
infile &fref;
@@ -2921,9 +3056,9 @@ create table &outds (rename=(
@brief Guess the primary key of a table
@details Tries to guess the primary key of a table based on the following logic:
* Columns with nulls are ignored
* Return only column combinations that provide unique results
* Start from one column, then move out to include composite keys of 2 to 6 columns
* Columns with nulls are ignored
* Return only column combinations that provide unique results
* Start from one column, then move out to include composite keys of 2 to 6 columns
The library of the target should be assigned before using this macro.
@@ -2969,7 +3104,7 @@ create table &outds (rename=(
/* get null count and row count */
%let tmpvar=%mf_getuniquename();
proc sql noprint;
create table _data_ as select
create table _data_ as select
count(*) as &tmpvar
%do i=1 %to &vcnt;
%let var=%scan(&vars,&i);
@@ -3003,10 +3138,10 @@ create table &outds (rename=(
%put &sysmacroname: &baseds has no combination of unique records! Exiting.;
%return;
%end;
/* now check cardinality */
proc sql noprint;
create table _data_ as select
create table _data_ as select
%do i=1 %to &ppkcnt;
%let var=%scan(&posspks,&i);
count(distinct &var) as &var
@@ -3130,7 +3265,7 @@ create table &outds (rename=(
%end;
%end;
%end;
%if &ppkcnt=4 %then %do;
%put &sysmacroname: No more PK guess possible;
%return;
@@ -3146,7 +3281,7 @@ create table &outds (rename=(
%let lev3=%scan(&posspks,&k);
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
%let lev4=%scan(&posspks,&l);
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
%do m=5 %to &ppkcnt;
%let lev5=%scan(&posspks,&m);
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then %do;
@@ -3168,7 +3303,7 @@ create table &outds (rename=(
%end;
%end;
%end;
%if &ppkcnt=5 %then %do;
%put &sysmacroname: No more PK guess possible;
%return;
@@ -3184,17 +3319,17 @@ create table &outds (rename=(
%let lev3=%scan(&posspks,&k);
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
%let lev4=%scan(&posspks,&l);
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
%do m=5 %to &ppkcnt;
%let lev5=%scan(&posspks,&m);
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then
%do n=6 %to &ppkcnt;
%let lev6=%scan(&posspks,&n);
%if &lev1 ne &lev6 & &lev2 ne &lev6 & &lev3 ne &lev6
& &lev4 ne &lev6 & &lev5 ne &lev6 %then
%if &lev1 ne &lev6 & &lev2 ne &lev6 & &lev3 ne &lev6
& &lev4 ne &lev6 & &lev5 ne &lev6 %then
%do;
/* check for four level uniqueness */
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4 &lev5 &lev6)
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4 &lev5 &lev6)
out=&tmpds noduprec;
by _all_;
run;
@@ -3213,7 +3348,7 @@ create table &outds (rename=(
%end;
%end;
%end;
%if &ppkcnt=6 %then %do;
%put &sysmacroname: No more PK guess possible;
%return;

View File

@@ -3,7 +3,7 @@
@brief Returns the engine type of a SAS library
@details Usage:
%put %mf_getEngine(SASHELP);
%put %mf_getengine(SASHELP);
returns:
> V9
@@ -21,9 +21,10 @@
@version 9.2
@author Allan Bowe
**/
%macro mf_getEngine(libref
**/ /** \cond */
%macro mf_getengine(libref
)/*/STORE SOURCE*/;
%local dsid engnum rc engine;
@@ -42,3 +43,5 @@
&engine
%mend;
/** \endcond */

121
base/mp_csv2ds.sas Normal file
View File

@@ -0,0 +1,121 @@
/**
@file mp_csv2ds.sas
@brief Efficient import of arbitrary CSV using a dataset as template
@details Used to import relevant columns from a large CSV using
a dataset to provide the types and lengths. Assumes that a header
row is provided, and datarows start on line 2. Extra columns in
both the CSV and base dataset are ignored.
Usage:
filename mycsv temp;
data _null_;
file mycsv;
put 'name,age,nickname';
put 'John,48,Jonny';
put 'Jennifer,23,Jen';
run;
%mp_csv2ds(inref=mycsv,outds=myds,baseds=sashelp.class)
@param inref= fileref to the CSV
@param outds= output ds (lib.ds format)
@param view= Set to YES or NO to determine whether the output should be
a view or not. Default is NO (not a view).
@param baseds= Template dataset on which to create the input statement.
Is used to determine types, lengths, and any informats.
@version 9.2
@author Allan Bowe
**/
%macro mp_csv2ds(inref=0,outds=0,baseds=0,view=NO);
%if &inref=0 %then %do;
%put %str(ERR)OR: the INREF variable must be provided;
%let syscc=4;
%abort;
%end;
%if %superq(outds)=0 %then %do;
%put %str(ERR)OR: the OUTDS variable must be provided;
%let syscc=4;
%return;
%end;
%if &baseds=0 %then %do;
%put %str(ERR)OR: the BASEDS variable must be provided;
%let syscc=4;
%return;
%end;
%if %sysfunc(exist(&BASEDS)) ne 1 & %sysfunc(exist(&BASEDS,VIEW)) ne 1 %then %do;
%put %str(ERR)OR: the BASEDS dataset (&baseds) needs to be assigned, and to exist;
%let syscc=4;
%return;
%end;
/* get the variables in the CSV */
data _data_;
infile &inref;
input;
length name $32;
do i=1 to countc(_infile_,',')+1;
name=upcase(scan(_infile_,i,','));
output;
end;
stop;
run;
%local csv_vars;%let csv_vars=&syslast;
/* get the variables in the dataset */
proc contents noprint data=&baseds
out=_data_ (keep=name type length format: informat);
run;
%local base_vars; %let base_vars=&syslast;
proc sql undo_policy=none;
create table &csv_vars as
select a.*
,b.type
,b.length
,b.format
,b.formatd
,b.formatl
,b.informat
from &csv_vars a
left join &base_vars b
on a.name=upcase(b.name)
order by i;
/* prepare the input statement */
%local instat dropvars;
data _null_;
set &syslast end=last;
length in dropvars $32767;
retain in dropvars;
if missing(type) then do;
informat='$1.';
dropvars=catx(' ',dropvars,name);
end;
else if missing(informat) then do;
if type=1 then informat='best.';
else informat=cats('$',length,'.');
end;
else informat=cats(informat,'.');
in=catx(' ',in,name,':',informat);
if last then do;
call symputx('instat',in,'l');
call symputx('dropvars',dropvars,'l');
end;
run;
/* import the CSV */
data &outds
%if %upcase(&view)=YES %then %do;
/view=&outds
%end;
;
infile &inref dsd firstobs=2;
input &instat;
drop &dropvars;
run;
%mend;

View File

@@ -1,17 +1,17 @@
/**
@file
@brief Returns all files and subdirectories within a specified parent
@details When used with getattrs=NO, is not OS specific (uses dopen / dread).
@details When used with getattrs=NO, is not OS specific (uses dopen / dread).
If getattrs=YES then the doptname / foptname functions are used to scan all
properties - any characters that are not valid in a SAS name (v7) are simply
properties - any characters that are not valid in a SAS name (v7) are simply
stripped, and the table is transposed so theat each property is a column
and there is one file per row. An attempt is made to get all properties
and there is one file per row. An attempt is made to get all properties
whether a file or folder, but some files/folders cannot be accessed, and so
not all properties can / will be populated.
Credit for the rename approach:
https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
usage:
@@ -20,21 +20,25 @@
%mp_dirlist(outds=cwdfileprops, getattrs=YES)
@warning In a Unix environment, the existence of a named pipe will cause this
%mp_dirlist(fref=MYFREF)
@warning In a Unix environment, the existence of a named pipe will cause this
macro to hang. Therefore this tool should be used with caution in a SAS 9 web
application, as it can use up all available multibridge sessions if requests
are resubmitted.
If anyone finds a way to positively identify a named pipe using SAS (without
If anyone finds a way to positively identify a named pipe using SAS (without
X CMD) do please raise an issue!
@param path= for which to return contents
@param fref= Provide a DISK engine fileref as an alternative to PATH
@param outds= the output dataset to create
@param getattrs= YES/NO (default=NO). Uses doptname and foptname to return
all attributes for each file / folder.
@param getattrs= YES/NO (default=NO). Uses doptname and foptname to return
all attributes for each file / folder.
@returns outds contains the following variables:
- directory (containing folder)
- file_or_folder (file / folder)
- filepath (path/to/file.name)
- filename (just the file name)
@@ -47,18 +51,26 @@
**/
%macro mp_dirlist(path=%sysfunc(pathname(work))
, fref=0
, outds=work.mp_dirlist
, getattrs=NO
)/*/STORE SOURCE*/;
%let getattrs=%upcase(&getattrs)XX;
data &outds (compress=no keep=file_or_folder filepath filename ext msg);
length filepath $500 fref fref2 $8 file_or_folder $6 filename $80 ext $20 msg $200;
rc = filename(fref, "&path");
data &outds (compress=no keep=file_or_folder filepath filename ext msg directory);
length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80 ext $20 msg $200;
%if &fref=0 %then %do;
rc = filename(fref, "&path");
%end;
%else %do;
fref="&fref";
rc=0;
%end;
if rc = 0 then do;
did = dopen(fref);
directory=dinfo(did,'Directory');
if did=0 then do;
putlog "NOTE: This directory is empty - &path";
putlog "NOTE: This directory is empty - " directory;
msg=sysmsg();
put _all_;
stop;
@@ -73,7 +85,8 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg);
dnum = dnum(did);
do i = 1 to dnum;
filename = dread(did, i);
rc = filename(fref2, "&path/"!!filename);
filepath=cats(directory,'/',filename);
rc = filename(fref2,filepath);
midd=dopen(fref2);
dmsg=sysmsg();
if did > 0 then file_or_folder='folder';
@@ -82,12 +95,12 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg);
fmsg=sysmsg();
if midf > 0 then file_or_folder='file';
rc=fclose(midf);
if index(fmsg,'File is in use') or index(dmsg,'is not a directory')
if index(fmsg,'File is in use') or index(dmsg,'is not a directory')
then file_or_folder='file';
else if index(fmsg, 'Insufficient authorization') then file_or_folder='file';
else if file_or_folder='' then file_or_folder='locked';
if file_or_folder='file' then do;
ext = prxchange('s/.*\.{1,1}(.*)/$1/', 1, filename);
if filename = ext then ext = ' ';
@@ -96,7 +109,6 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg);
ext='';
file_or_folder='folder';
end;
filepath="&path/"!!filename;
output;
end;
rc = dclose(did);
@@ -120,7 +132,7 @@ run;
else do i=1 to foptnum(fid);
infoname=foptname(fid,i);
infoval=finfo(fid,infoname);
sasname=compress(infoname, '_', 'adik');
sasname=compress(infoname, '_', 'adik');
if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
if upcase(sasname) ne 'FILENAME' then output;
end;
@@ -137,7 +149,7 @@ run;
else do i=1 to doptnum(fid);
infoname=doptname(fid,i);
infoval=dinfo(fid,infoname);
sasname=compress(infoname, '_', 'adik');
sasname=compress(infoname, '_', 'adik');
if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
if upcase(sasname) ne 'FILENAME' then output;
end;

View File

@@ -30,7 +30,6 @@
datetime2 format or regular decimal type
@version 9.3
@author Allan Bowe
@source https://github.com/sasjs/core
**/
%macro mp_getddl(libref,ds,fref=getddl,flavour=SAS,showlog=NO,schema=
@@ -132,7 +131,7 @@ run;
%let curds=%scan(&dsnlist,&x);
data _null_;
file &fref mod;
length nm lab $1024;
length nm lab $1024 typ $20;
set &colinfo (where=(upcase(memname)="&curds")) end=last;
if _n_=1 then do;
@@ -146,10 +145,12 @@ run;
end;
else put " ,"@@;
if length(format)>1 then fmt=" format="!!cats(format);
len=" length="!!cats(length);
lab=" label="!!quote(trim(label));
if length(label)>1 then lab=" label="!!quote(trim(label));
if notnull='yes' then notnul=' not null';
put name type len fmt notnul lab;
if type='char' then typ=cats('char(',length,')');
else if length ne 8 then typ='num length='!!left(length);
else typ='num';
put name typ fmt notnul lab;
run;
/* Extra step for data constraints */
@@ -159,7 +160,7 @@ run;
file &fref mod;
put ');';
run;
/* Create Unique Indexes, but only if they were not already defined within the Constraints section. */
data _null_;
*length ds $128;
@@ -339,7 +340,7 @@ run;
%end;
%end;
%if &showlog=YES %then %do;
%if %upcase(&showlog)=YES %then %do;
options ps=max;
data _null_;
infile &fref;

View File

@@ -3,9 +3,9 @@
@brief Guess the primary key of a table
@details Tries to guess the primary key of a table based on the following logic:
* Columns with nulls are ignored
* Return only column combinations that provide unique results
* Start from one column, then move out to include composite keys of 2 to 6 columns
* Columns with nulls are ignored
* Return only column combinations that provide unique results
* Start from one column, then move out to include composite keys of 2 to 6 columns
The library of the target should be assigned before using this macro.
@@ -51,7 +51,7 @@
/* get null count and row count */
%let tmpvar=%mf_getuniquename();
proc sql noprint;
create table _data_ as select
create table _data_ as select
count(*) as &tmpvar
%do i=1 %to &vcnt;
%let var=%scan(&vars,&i);
@@ -85,10 +85,10 @@
%put &sysmacroname: &baseds has no combination of unique records! Exiting.;
%return;
%end;
/* now check cardinality */
proc sql noprint;
create table _data_ as select
create table _data_ as select
%do i=1 %to &ppkcnt;
%let var=%scan(&posspks,&i);
count(distinct &var) as &var
@@ -212,7 +212,7 @@
%end;
%end;
%end;
%if &ppkcnt=4 %then %do;
%put &sysmacroname: No more PK guess possible;
%return;
@@ -228,7 +228,7 @@
%let lev3=%scan(&posspks,&k);
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
%let lev4=%scan(&posspks,&l);
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
%do m=5 %to &ppkcnt;
%let lev5=%scan(&posspks,&m);
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then %do;
@@ -250,7 +250,7 @@
%end;
%end;
%end;
%if &ppkcnt=5 %then %do;
%put &sysmacroname: No more PK guess possible;
%return;
@@ -266,17 +266,17 @@
%let lev3=%scan(&posspks,&k);
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
%let lev4=%scan(&posspks,&l);
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
%do m=5 %to &ppkcnt;
%let lev5=%scan(&posspks,&m);
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then
%do n=6 %to &ppkcnt;
%let lev6=%scan(&posspks,&n);
%if &lev1 ne &lev6 & &lev2 ne &lev6 & &lev3 ne &lev6
& &lev4 ne &lev6 & &lev5 ne &lev6 %then
%if &lev1 ne &lev6 & &lev2 ne &lev6 & &lev3 ne &lev6
& &lev4 ne &lev6 & &lev5 ne &lev6 %then
%do;
/* check for four level uniqueness */
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4 &lev5 &lev6)
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4 &lev5 &lev6)
out=&tmpds noduprec;
by _all_;
run;
@@ -295,7 +295,7 @@
%end;
%end;
%end;
%if &ppkcnt=6 %then %do;
%put &sysmacroname: No more PK guess possible;
%return;

View File

@@ -36,5 +36,9 @@ echo 'core.sasjs.io' > CNAME
git add *
git commit -m "build.sh build on $(date +%F:%H:%M:%S)"
git push
npx sitemap-generator-cli https://core.sasjs.io
git add *
git commit -m "adding sitemap"
git push
echo "check it out: https://sasjs.github.io/core.github.io/files.html"