@sasjs/utils 2.48.2 → 2.49.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fs/generateCompileProgram.d.ts +1 -0
- package/fs/generateCompileProgram.js +154 -0
- package/fs/generateCompileProgram.js.map +1 -0
- package/fs/hash.d.ts +22 -0
- package/fs/hash.js +225 -0
- package/fs/hash.js.map +1 -0
- package/fs/index.d.ts +3 -0
- package/fs/index.js +13 -0
- package/fs/index.js.map +1 -0
- package/fs/internal/helper.d.ts +4 -0
- package/fs/internal/helper.js +125 -0
- package/fs/internal/helper.js.map +1 -0
- package/fs/sync.d.ts +6 -0
- package/fs/sync.js +208 -0
- package/fs/sync.js.map +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/macros/mf_abort.sas +27 -0
- package/macros/mf_dedup.sas +53 -0
- package/macros/mf_deletefile.sas +31 -0
- package/macros/mf_existds.sas +30 -0
- package/macros/mf_existfeature.sas +59 -0
- package/macros/mf_existfileref.sas +33 -0
- package/macros/mf_existfunction.sas +37 -0
- package/macros/mf_existvar.sas +39 -0
- package/macros/mf_existvarlist.sas +56 -0
- package/macros/mf_fmtdttm.sas +42 -0
- package/macros/mf_getapploc.sas +78 -0
- package/macros/mf_getattrc.sas +34 -0
- package/macros/mf_getattrn.sas +34 -0
- package/macros/mf_getengine.sas +54 -0
- package/macros/mf_getfilesize.sas +64 -0
- package/macros/mf_getfmtlist.sas +61 -0
- package/macros/mf_getfmtname.sas +44 -0
- package/macros/mf_getkeyvalue.sas +32 -0
- package/macros/mf_getplatform.sas +75 -0
- package/macros/mf_getquotedstr.sas +57 -0
- package/macros/mf_getschema.sas +43 -0
- package/macros/mf_getuniquefileref.sas +60 -0
- package/macros/mf_getuniquelibref.sas +57 -0
- package/macros/mf_getuniquename.sas +22 -0
- package/macros/mf_getuser.sas +44 -0
- package/macros/mf_getvalue.sas +36 -0
- package/macros/mf_getvarcount.sas +49 -0
- package/macros/mf_getvarformat.sas +72 -0
- package/macros/mf_getvarlen.sas +56 -0
- package/macros/mf_getvarlist.sas +74 -0
- package/macros/mf_getvarnum.sas +58 -0
- package/macros/mf_getvartype.sas +52 -0
- package/macros/mf_getxengine.sas +43 -0
- package/macros/mf_increment.sas +29 -0
- package/macros/mf_isblank.sas +27 -0
- package/macros/mf_isdir.sas +34 -0
- package/macros/mf_isint.sas +36 -0
- package/macros/mf_islibds.sas +40 -0
- package/macros/mf_loc.sas +29 -0
- package/macros/mf_mkdir.sas +67 -0
- package/macros/mf_mval.sas +19 -0
- package/macros/mf_nobs.sas +26 -0
- package/macros/mf_trimstr.sas +50 -0
- package/macros/mf_uid.sas +21 -0
- package/macros/mf_verifymacvars.sas +72 -0
- package/macros/mf_wordsinstr1andstr2.sas +53 -0
- package/macros/mf_wordsinstr1butnotstr2.sas +54 -0
- package/macros/mf_writefile.sas +67 -0
- package/macros/mp_abort.sas +301 -0
- package/macros/mp_appendfile.sas +57 -0
- package/macros/mp_applyformats.sas +181 -0
- package/macros/mp_assert.sas +56 -0
- package/macros/mp_assertcols.sas +145 -0
- package/macros/mp_assertcolvals.sas +168 -0
- package/macros/mp_assertdsobs.sas +121 -0
- package/macros/mp_assertscope.sas +147 -0
- package/macros/mp_base64copy.sas +117 -0
- package/macros/mp_binarycopy.sas +79 -0
- package/macros/mp_chop.sas +194 -0
- package/macros/mp_cleancsv.sas +78 -0
- package/macros/mp_cntlout.sas +85 -0
- package/macros/mp_copyfolder.sas +84 -0
- package/macros/mp_coretable.sas +73 -0
- package/macros/mp_createconstraints.sas +70 -0
- package/macros/mp_createwebservice.sas +32 -0
- package/macros/mp_csv2ds.sas +140 -0
- package/macros/mp_deleteconstraints.sas +52 -0
- package/macros/mp_deletefolder.sas +70 -0
- package/macros/mp_dictionary.sas +52 -0
- package/macros/mp_dirlist.sas +263 -0
- package/macros/mp_distinctfmtvalues.sas +50 -0
- package/macros/mp_dropmembers.sas +46 -0
- package/macros/mp_ds2cards.sas +291 -0
- package/macros/mp_ds2csv.sas +212 -0
- package/macros/mp_ds2ddl.sas +30 -0
- package/macros/mp_ds2fmtds.sas +105 -0
- package/macros/mp_ds2inserts.sas +180 -0
- package/macros/mp_ds2md.sas +103 -0
- package/macros/mp_ds2squeeze.sas +120 -0
- package/macros/mp_dsmeta.sas +110 -0
- package/macros/mp_filtercheck.sas +251 -0
- package/macros/mp_filtergenerate.sas +105 -0
- package/macros/mp_filterstore.sas +254 -0
- package/macros/mp_filtervalidate.sas +105 -0
- package/macros/mp_getcols.sas +67 -0
- package/macros/mp_getconstraints.sas +115 -0
- package/macros/mp_getdbml.sas +335 -0
- package/macros/mp_getddl.sas +400 -0
- package/macros/mp_getformats.sas +124 -0
- package/macros/mp_getmaxvarlengths.sas +130 -0
- package/macros/mp_getpk.sas +281 -0
- package/macros/mp_gsubfile.sas +58 -0
- package/macros/mp_guesspk.sas +332 -0
- package/macros/mp_hashdataset.sas +91 -0
- package/macros/mp_hashdirectory.sas +162 -0
- package/macros/mp_include.sas +106 -0
- package/macros/mp_init.sas +76 -0
- package/macros/mp_jsonout.sas +406 -0
- package/macros/mp_lib2cards.sas +78 -0
- package/macros/mp_lib2inserts.sas +77 -0
- package/macros/mp_loadformat.sas +303 -0
- package/macros/mp_lockanytable.sas +243 -0
- package/macros/mp_lockfilecheck.sas +103 -0
- package/macros/mp_makedata.sas +107 -0
- package/macros/mp_md5.sas +58 -0
- package/macros/mp_perflog.sas +43 -0
- package/macros/mp_prevobs.sas +88 -0
- package/macros/mp_recursivejoin.sas +90 -0
- package/macros/mp_replace.sas +151 -0
- package/macros/mp_reseterror.sas +27 -0
- package/macros/mp_resetoption.sas +39 -0
- package/macros/mp_retainedkey.sas +247 -0
- package/macros/mp_runddl.sas +49 -0
- package/macros/mp_searchcols.sas +103 -0
- package/macros/mp_searchdata.sas +131 -0
- package/macros/mp_setkeyvalue.sas +55 -0
- package/macros/mp_sortinplace.sas +122 -0
- package/macros/mp_stackdiffs.sas +592 -0
- package/macros/mp_storediffs.sas +230 -0
- package/macros/mp_stprequests.sas +74 -0
- package/macros/mp_streamfile.sas +212 -0
- package/macros/mp_testjob.sas +92 -0
- package/macros/mp_testservice.sas +39 -0
- package/macros/mp_testwritespeedlibrary.sas +59 -0
- package/macros/mp_tree.sas +71 -0
- package/macros/mp_unzip.sas +90 -0
- package/macros/mp_updatevarlength.sas +93 -0
- package/macros/mp_validatecol.sas +97 -0
- package/macros/mp_wait4file.sas +35 -0
- package/macros/mp_webin.sas +59 -0
- package/macros/mp_zip.sas +85 -0
- package/package.json +5 -2
- package/scripts/copySASjsCore.ts +36 -0
- package/types/hash.d.ts +9 -0
- package/types/hash.js +3 -0
- package/types/hash.js.map +1 -0
- package/types/index.d.ts +1 -0
- package/types/index.js +1 -0
- package/types/index.js.map +1 -1
- package/utils/index.d.ts +1 -1
- package/utils/index.js +2 -1
- package/utils/index.js.map +1 -1
- package/utils/utils.d.ts +1 -0
- package/utils/utils.js +7 -1
- package/utils/utils.js.map +1 -1
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Extract the primary key fields from a table or library
|
|
4
|
+
@details Examines the constraints to identify primary key fields - indicated
|
|
5
|
+
by an explicit PK constraint, or a unique index that is also NOT NULL.
|
|
6
|
+
|
|
7
|
+
Can be executed at both table and library level. Supports both BASE engine
|
|
8
|
+
libraries and SQL Server.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
|
|
12
|
+
proc sql;
|
|
13
|
+
create table work.example(
|
|
14
|
+
TX_FROM float format=datetime19.,
|
|
15
|
+
DD_TYPE char(16),
|
|
16
|
+
DD_SOURCE char(2048),
|
|
17
|
+
DD_SHORTDESC char(256),
|
|
18
|
+
constraint pk primary key(tx_from, dd_type,dd_source),
|
|
19
|
+
constraint unq unique(tx_from, dd_type),
|
|
20
|
+
constraint nnn not null(DD_SHORTDESC)
|
|
21
|
+
);
|
|
22
|
+
%mp_getpk(work,ds=example)
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
|
|
26
|
+
|libref:$8.|dsn:$32.|memtype:$8.|dbms_memtype:$32.|typemem:$8.|memlabel:$256.|nvar:best.|compress:$8.|pk_fields:$512.|
|
|
27
|
+
|---|---|---|---|---|---|---|---|---|
|
|
28
|
+
|WORK|EXAMPLE|DATA| |DATA| |4|NO|TX_FROM DD_TYPE DD_SOURCE|
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@param [in] lib The libref to examine
|
|
32
|
+
@param [in] ds= (0) Select the dataset to examine, else use 0 for all tables
|
|
33
|
+
@param [in] mdebug= (0) Set to 1 to preserve temp tables, print var values etc
|
|
34
|
+
@param [out] outds= (work.mp_getpk) The name of the output table to create.
|
|
35
|
+
|
|
36
|
+
<h4> SAS Macros </h4>
|
|
37
|
+
@li mf_existfeature.sas
|
|
38
|
+
@li mf_getengine.sas
|
|
39
|
+
@li mf_getschema.sas
|
|
40
|
+
@li mp_dropmembers.sas
|
|
41
|
+
@li mp_getconstraints.sas
|
|
42
|
+
|
|
43
|
+
<h4> Related Macros </h4>
|
|
44
|
+
@li mp_getpk.test.sas
|
|
45
|
+
@li mp_guesspk.sas
|
|
46
|
+
|
|
47
|
+
@version 9.3
|
|
48
|
+
@author Macro People Ltd
|
|
49
|
+
**/
|
|
50
|
+
|
|
51
|
+
%macro mp_getpk(
|
|
52
|
+
lib,
|
|
53
|
+
ds=0,
|
|
54
|
+
outds=work.mp_getpk,
|
|
55
|
+
mdebug=0
|
|
56
|
+
)/*/STORE SOURCE*/;
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
%local engine schema ds1 ds2 ds3 dsn tabs1 tabs2 sum pk4sure pkdefault finalpks
|
|
60
|
+
pkfromindex;
|
|
61
|
+
|
|
62
|
+
%let lib=%upcase(&lib);
|
|
63
|
+
%let ds=%upcase(&ds);
|
|
64
|
+
%let engine=%mf_getengine(&lib);
|
|
65
|
+
%let schema=%mf_getschema(&lib);
|
|
66
|
+
|
|
67
|
+
%let ds1=%mf_getuniquename(prefix=getpk_ds1);
|
|
68
|
+
%let ds2=%mf_getuniquename(prefix=getpk_ds2);
|
|
69
|
+
%let ds3=%mf_getuniquename(prefix=getpk_ds3);
|
|
70
|
+
%let tabs1=%mf_getuniquename(prefix=getpk_tabs1);
|
|
71
|
+
%let tabs2=%mf_getuniquename(prefix=getpk_tabs2);
|
|
72
|
+
%let sum=%mf_getuniquename(prefix=getpk_sum);
|
|
73
|
+
%let pk4sure=%mf_getuniquename(prefix=getpk_pk4sure);
|
|
74
|
+
%let pkdefault=%mf_getuniquename(prefix=getpk_pkdefault);
|
|
75
|
+
%let pkfromindex=%mf_getuniquename(prefix=getpk_pkfromindex);
|
|
76
|
+
%let finalpks=%mf_getuniquename(prefix=getpk_finalpks);
|
|
77
|
+
|
|
78
|
+
%local dbg;
|
|
79
|
+
%if &mdebug=1 %then %do;
|
|
80
|
+
%put &sysmacroname entry vars:;
|
|
81
|
+
%put _local_;
|
|
82
|
+
%end;
|
|
83
|
+
%else %let dbg=*;
|
|
84
|
+
|
|
85
|
+
proc sql;
|
|
86
|
+
create table &ds1 as
|
|
87
|
+
select libname as libref
|
|
88
|
+
,upcase(memname) as dsn
|
|
89
|
+
,memtype
|
|
90
|
+
,upcase(name) as name
|
|
91
|
+
,type
|
|
92
|
+
,length
|
|
93
|
+
,varnum
|
|
94
|
+
,label
|
|
95
|
+
,format
|
|
96
|
+
,idxusage
|
|
97
|
+
,notnull
|
|
98
|
+
from dictionary.columns
|
|
99
|
+
where upcase(libname)="&lib"
|
|
100
|
+
%if &ds ne 0 %then %do;
|
|
101
|
+
and upcase(memname)="&ds"
|
|
102
|
+
%end;
|
|
103
|
+
;
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
%if &engine=SQLSVR %then %do;
|
|
107
|
+
proc sql;
|
|
108
|
+
connect using &lib;
|
|
109
|
+
create table work.&ds2 as
|
|
110
|
+
select * from connection to &lib(
|
|
111
|
+
select
|
|
112
|
+
s.name as SchemaName,
|
|
113
|
+
t.name as memname,
|
|
114
|
+
tc.name as name,
|
|
115
|
+
ic.key_ordinal as KeyOrderNr
|
|
116
|
+
from
|
|
117
|
+
sys.schemas s
|
|
118
|
+
inner join sys.tables t on s.schema_id=t.schema_id
|
|
119
|
+
inner join sys.indexes i on t.object_id=i.object_id
|
|
120
|
+
inner join sys.index_columns ic on i.object_id=ic.object_id
|
|
121
|
+
and i.index_id=ic.index_id
|
|
122
|
+
inner join sys.columns tc on ic.object_id=tc.object_id
|
|
123
|
+
and ic.column_id=tc.column_id
|
|
124
|
+
where i.is_primary_key=1
|
|
125
|
+
and s.name=%str(%')&schema%str(%')
|
|
126
|
+
order by t.name, ic.key_ordinal ;
|
|
127
|
+
);disconnect from &lib;
|
|
128
|
+
create table &ds3 as
|
|
129
|
+
select a.*
|
|
130
|
+
,case when b.name is not null then 1 else 0 end as pk_ind
|
|
131
|
+
from work.&ds1 a
|
|
132
|
+
left join work.&ds2 b
|
|
133
|
+
on a.dsn=b.memname
|
|
134
|
+
and upcase(a.name)=upcase(b.name)
|
|
135
|
+
order by libref,dsn;
|
|
136
|
+
%end;
|
|
137
|
+
%else %do;
|
|
138
|
+
|
|
139
|
+
%if &ds = 0 %then %let dsn=;
|
|
140
|
+
|
|
141
|
+
/* get all constraints, in constraint order*/
|
|
142
|
+
%mp_getconstraints(lib=&lib,ds=&dsn,outds=work.&ds2)
|
|
143
|
+
|
|
144
|
+
/* extract cols that are clearly primary keys */
|
|
145
|
+
proc sql;
|
|
146
|
+
create table &pk4sure as
|
|
147
|
+
select libref
|
|
148
|
+
,table_name
|
|
149
|
+
,constraint_name
|
|
150
|
+
,constraint_order
|
|
151
|
+
,column_name as name
|
|
152
|
+
from work.&ds2
|
|
153
|
+
where constraint_type='PRIMARY'
|
|
154
|
+
order by 1,2,3,4;
|
|
155
|
+
|
|
156
|
+
/* extract unique constraints where every col is also NOT NULL */
|
|
157
|
+
proc sql;
|
|
158
|
+
create table &sum as
|
|
159
|
+
select a.libref
|
|
160
|
+
,a.table_name
|
|
161
|
+
,a.constraint_name
|
|
162
|
+
,count(a.column_name) as unq_cnt
|
|
163
|
+
,count(b.column_name) as nul_cnt
|
|
164
|
+
from work.&ds2(where=(constraint_type ='UNIQUE')) a
|
|
165
|
+
left join work.&ds2(where=(constraint_type ='NOT NULL')) b
|
|
166
|
+
on a.libref=b.libref
|
|
167
|
+
and a.table_name=b.table_name
|
|
168
|
+
and a.column_name=b.column_name
|
|
169
|
+
group by 1,2,3
|
|
170
|
+
having unq_cnt=nul_cnt;
|
|
171
|
+
|
|
172
|
+
/* extract cols from the relevant unique constraints */
|
|
173
|
+
create table &pkdefault as
|
|
174
|
+
select a.libref
|
|
175
|
+
,a.table_name
|
|
176
|
+
,a.constraint_name
|
|
177
|
+
,b.constraint_order
|
|
178
|
+
,b.column_name as name
|
|
179
|
+
from &sum a
|
|
180
|
+
left join &ds2(where=(constraint_type ='UNIQUE')) b
|
|
181
|
+
on a.libref=b.libref
|
|
182
|
+
and a.table_name=b.table_name
|
|
183
|
+
and a.constraint_name=b.constraint_name
|
|
184
|
+
order by 1,2,3,4;
|
|
185
|
+
|
|
186
|
+
/* extract cols from the relevant unique INDEXES */
|
|
187
|
+
create table &pkfromindex as
|
|
188
|
+
select libname as libref
|
|
189
|
+
,memname as table_name
|
|
190
|
+
,indxname as constraint_name
|
|
191
|
+
,indxpos as constraint_order
|
|
192
|
+
,name
|
|
193
|
+
from dictionary.indexes
|
|
194
|
+
where nomiss='yes' and unique='yes' and upcase(libname)="&lib"
|
|
195
|
+
%if &ds ne 0 %then %do;
|
|
196
|
+
and upcase(memname)="&ds"
|
|
197
|
+
%end;
|
|
198
|
+
order by 1,2,3,4;
|
|
199
|
+
|
|
200
|
+
/* create one table */
|
|
201
|
+
data &finalpks;
|
|
202
|
+
set &pkdefault &pk4sure &pkfromindex;
|
|
203
|
+
pk_ind=1;
|
|
204
|
+
/* if there are multiple unique constraints, take the first */
|
|
205
|
+
by libref table_name constraint_name;
|
|
206
|
+
retain keepme;
|
|
207
|
+
if first.table_name then keepme=1;
|
|
208
|
+
if first.constraint_name and not first.table_name then keepme=0;
|
|
209
|
+
if keepme=1;
|
|
210
|
+
run;
|
|
211
|
+
|
|
212
|
+
/* join back to starting table */
|
|
213
|
+
proc sql;
|
|
214
|
+
create table &ds3 as
|
|
215
|
+
select a.*
|
|
216
|
+
,b.constraint_order
|
|
217
|
+
,case when b.pk_ind=1 then 1 else 0 end as pk_ind
|
|
218
|
+
from work.&ds1 a
|
|
219
|
+
left join work.&finalpks b
|
|
220
|
+
on a.libref=b.libref
|
|
221
|
+
and a.dsn=b.table_name
|
|
222
|
+
and upcase(a.name)=upcase(b.name)
|
|
223
|
+
order by libref,dsn,constraint_order;
|
|
224
|
+
%end;
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
/* prepare tables */
|
|
228
|
+
proc sql;
|
|
229
|
+
create table work.&tabs1 as select
|
|
230
|
+
libname as libref
|
|
231
|
+
,upcase(memname) as dsn
|
|
232
|
+
,memtype
|
|
233
|
+
%if %mf_existfeature(DBMS_MEMTYPE)=1 %then %do;
|
|
234
|
+
,dbms_memtype
|
|
235
|
+
%end;
|
|
236
|
+
%else %do;
|
|
237
|
+
,'n/a' as dbms_memtype format=$32.
|
|
238
|
+
%end;
|
|
239
|
+
,typemem
|
|
240
|
+
,memlabel
|
|
241
|
+
,nvar
|
|
242
|
+
,compress
|
|
243
|
+
from dictionary.tables
|
|
244
|
+
where upcase(libname)="&lib"
|
|
245
|
+
%if &ds ne 0 %then %do;
|
|
246
|
+
and upcase(memname)="&ds"
|
|
247
|
+
%end;
|
|
248
|
+
;
|
|
249
|
+
data &tabs2;
|
|
250
|
+
set &ds3;
|
|
251
|
+
length pk_fields $512;
|
|
252
|
+
retain pk_fields;
|
|
253
|
+
by libref dsn constraint_order;
|
|
254
|
+
if first.dsn then pk_fields='';
|
|
255
|
+
if pk_ind=1 then pk_fields=catx(' ',pk_fields,name);
|
|
256
|
+
if last.dsn then output;
|
|
257
|
+
run;
|
|
258
|
+
|
|
259
|
+
proc sql;
|
|
260
|
+
create table &outds as
|
|
261
|
+
select a.libref
|
|
262
|
+
,a.dsn
|
|
263
|
+
,a.memtype
|
|
264
|
+
,a.dbms_memtype
|
|
265
|
+
,a.typemem
|
|
266
|
+
,a.memlabel
|
|
267
|
+
,a.nvar
|
|
268
|
+
,a.compress
|
|
269
|
+
,b.pk_fields
|
|
270
|
+
from work.&tabs1 a
|
|
271
|
+
left join work.&tabs2 b
|
|
272
|
+
on a.libref=b.libref
|
|
273
|
+
and a.dsn=b.dsn;
|
|
274
|
+
|
|
275
|
+
/* tidy up */
|
|
276
|
+
%mp_dropmembers(
|
|
277
|
+
&ds1 &ds2 &ds3 &dsn &tabs1 &tabs2 &sum &pk4sure &pkdefault &finalpks,
|
|
278
|
+
iftrue=(&mdebug=0)
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
%mend mp_getpk;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Performs a text substitution on a file
|
|
4
|
+
@details Makes use of the GSUB function in LUA to perform a text substitution
|
|
5
|
+
in a file - either in-place, or writing to a new location. The benefit of
|
|
6
|
+
using LUA is that the entire file can be loaded into a single variable,
|
|
7
|
+
thereby side stepping the 32767 character limit in a data step.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
|
|
11
|
+
%let file=%sysfunc(pathname(work))/file.txt;
|
|
12
|
+
%let str=replace/me;
|
|
13
|
+
%let rep=with/this;
|
|
14
|
+
data _null_;
|
|
15
|
+
file "&file";
|
|
16
|
+
put "&str";
|
|
17
|
+
run;
|
|
18
|
+
%mp_gsubfile(file=&file, patternvar=str, replacevar=rep)
|
|
19
|
+
data _null_;
|
|
20
|
+
infile "&file";
|
|
21
|
+
input;
|
|
22
|
+
list;
|
|
23
|
+
run;
|
|
24
|
+
|
|
25
|
+
@param file= (0) The file to perform the substitution on
|
|
26
|
+
@param patternvar= A macro variable containing the Lua
|
|
27
|
+
[pattern](https://www.lua.org/pil/20.2.html) to search for. Due to the use
|
|
28
|
+
of special (magic) characters in Lua patterns, it is safer to pass the NAME
|
|
29
|
+
of the macro variable containing the string, rather than the value itself.
|
|
30
|
+
@param replacevar= The name of the macro variable containing the replacement
|
|
31
|
+
_string_.
|
|
32
|
+
@param outfile= (0) The file to write the output to. If zero, then the file
|
|
33
|
+
is overwritten in-place.
|
|
34
|
+
|
|
35
|
+
<h4> SAS Macros </h4>
|
|
36
|
+
@li ml_gsubfile.sas
|
|
37
|
+
|
|
38
|
+
<h4> Related Macros </h4>
|
|
39
|
+
@li mp_gsubfile.test.sas
|
|
40
|
+
|
|
41
|
+
@version 9.4
|
|
42
|
+
@author Allan Bowe
|
|
43
|
+
**/
|
|
44
|
+
|
|
45
|
+
%macro mp_gsubfile(file=0,
|
|
46
|
+
patternvar=,
|
|
47
|
+
replacevar=,
|
|
48
|
+
outfile=0
|
|
49
|
+
)/*/STORE SOURCE*/;
|
|
50
|
+
|
|
51
|
+
%if "%substr(&sysver.XX,1,4)"="V.04" %then %do;
|
|
52
|
+
%put %str(ERR)OR: Viya 4 does not support the IO library in lua;
|
|
53
|
+
%return;
|
|
54
|
+
%end;
|
|
55
|
+
|
|
56
|
+
%ml_gsubfile()
|
|
57
|
+
|
|
58
|
+
%mend mp_gsubfile;
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Guess the primary key of a table
|
|
4
|
+
@details Tries to guess the primary key of a table based on the following
|
|
5
|
+
logic:
|
|
6
|
+
|
|
7
|
+
* Columns with nulls are ignored
|
|
8
|
+
* Return only column combinations that provide unique results
|
|
9
|
+
* Start from one column, then move out to composite keys of 2 to 6 columns
|
|
10
|
+
|
|
11
|
+
The library of the target should be assigned before using this macro.
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
|
|
15
|
+
filename mc url
|
|
16
|
+
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
|
17
|
+
%inc mc;
|
|
18
|
+
%mp_guesspk(sashelp.class,outds=classpks)
|
|
19
|
+
|
|
20
|
+
@param [in] baseds The dataset to analyse
|
|
21
|
+
@param [out] outds= The output dataset to contain the possible PKs
|
|
22
|
+
@param [in] max_guesses= (3) The total number of possible primary keys to
|
|
23
|
+
generate. A table may have multiple (unlikely) PKs, so no need to list them
|
|
24
|
+
all.
|
|
25
|
+
@param [in] min_rows= (5) The minimum number of rows a table should have in
|
|
26
|
+
order to try and guess the PK.
|
|
27
|
+
@param [in] ignore_cols (0) Space seperated list of columns which you are
|
|
28
|
+
sure are not part of the primary key (helps to avoid false positives)
|
|
29
|
+
@param [in] mdebug= Set to 1 to enable DEBUG messages and preserve outputs
|
|
30
|
+
|
|
31
|
+
<h4> SAS Macros </h4>
|
|
32
|
+
@li mf_getvarlist.sas
|
|
33
|
+
@li mf_getuniquename.sas
|
|
34
|
+
@li mf_wordsInstr1butnotstr2.sas
|
|
35
|
+
@li mf_nobs.sas
|
|
36
|
+
|
|
37
|
+
<h4> Related Macros </h4>
|
|
38
|
+
@li mp_getpk.sas
|
|
39
|
+
|
|
40
|
+
@version 9.3
|
|
41
|
+
@author Allan Bowe
|
|
42
|
+
|
|
43
|
+
**/
|
|
44
|
+
|
|
45
|
+
%macro mp_guesspk(baseds
|
|
46
|
+
,outds=mp_guesspk
|
|
47
|
+
,max_guesses=3
|
|
48
|
+
,min_rows=5
|
|
49
|
+
,ignore_cols=0
|
|
50
|
+
,mdebug=0
|
|
51
|
+
)/*/STORE SOURCE*/;
|
|
52
|
+
%local dbg;
|
|
53
|
+
%if &mdebug=1 %then %do;
|
|
54
|
+
%put &sysmacroname entry vars:;
|
|
55
|
+
%put _local_;
|
|
56
|
+
%end;
|
|
57
|
+
%else %let dbg=*;
|
|
58
|
+
|
|
59
|
+
/* declare local vars */
|
|
60
|
+
%local var vars vcnt i j k l tmpvar tmpds rows posspks ppkcnt;
|
|
61
|
+
%let vars=%upcase(%mf_getvarlist(&baseds));
|
|
62
|
+
%let vars=%mf_wordsInStr1ButNotStr2(str1=&vars,str2=%upcase(&ignore_cols));
|
|
63
|
+
%let vcnt=%sysfunc(countw(&vars));
|
|
64
|
+
|
|
65
|
+
%if &vcnt=0 %then %do;
|
|
66
|
+
%put &sysmacroname: &baseds has no variables! Exiting.;
|
|
67
|
+
%return;
|
|
68
|
+
%end;
|
|
69
|
+
|
|
70
|
+
/* get null count and row count */
|
|
71
|
+
%let tmpvar=%mf_getuniquename();
|
|
72
|
+
proc sql noprint;
|
|
73
|
+
create table _data_ as select
|
|
74
|
+
count(*) as &tmpvar
|
|
75
|
+
%do i=1 %to &vcnt;
|
|
76
|
+
%let var=%scan(&vars,&i);
|
|
77
|
+
,sum(case when &var is missing then 1 else 0 end) as &var
|
|
78
|
+
%end;
|
|
79
|
+
from &baseds;
|
|
80
|
+
|
|
81
|
+
/* transpose table and scan for not null cols */
|
|
82
|
+
proc transpose;
|
|
83
|
+
data _null_;
|
|
84
|
+
set &syslast end=last;
|
|
85
|
+
length vars $32767;
|
|
86
|
+
retain vars ;
|
|
87
|
+
if _name_="&tmpvar" then call symputx('rows',col1,'l');
|
|
88
|
+
else if col1=0 then vars=catx(' ',vars,_name_);
|
|
89
|
+
if last then call symputx('posspks',vars,'l');
|
|
90
|
+
run;
|
|
91
|
+
|
|
92
|
+
%let ppkcnt=%sysfunc(countw(&posspks));
|
|
93
|
+
%if &ppkcnt=0 %then %do;
|
|
94
|
+
%put &sysmacroname: &baseds has no non-missing variables! Exiting.;
|
|
95
|
+
%return;
|
|
96
|
+
%end;
|
|
97
|
+
|
|
98
|
+
proc sort data=&baseds(keep=&posspks) out=_data_ noduprec;
|
|
99
|
+
by _all_;
|
|
100
|
+
run;
|
|
101
|
+
%local pkds; %let pkds=&syslast;
|
|
102
|
+
|
|
103
|
+
%if &rows > %mf_nobs(&pkds) %then %do;
|
|
104
|
+
%put &sysmacroname: &baseds has no combination of unique records! Exiting.;
|
|
105
|
+
%return;
|
|
106
|
+
%end;
|
|
107
|
+
|
|
108
|
+
/* now check cardinality */
|
|
109
|
+
proc sql noprint;
|
|
110
|
+
create table _data_ as select
|
|
111
|
+
%do i=1 %to &ppkcnt;
|
|
112
|
+
%let var=%scan(&posspks,&i);
|
|
113
|
+
count(distinct &var) as &var
|
|
114
|
+
%if &i<&ppkcnt %then ,;
|
|
115
|
+
%end;
|
|
116
|
+
from &pkds;
|
|
117
|
+
|
|
118
|
+
/* transpose and sort by cardinality */
|
|
119
|
+
proc transpose;
|
|
120
|
+
proc sort; by descending col1;
|
|
121
|
+
run;
|
|
122
|
+
|
|
123
|
+
/* create initial PK list and re-order posspks list */
|
|
124
|
+
data &outds(keep=pkguesses);
|
|
125
|
+
length pkguesses $5000 vars $5000;
|
|
126
|
+
set &syslast end=last;
|
|
127
|
+
retain vars ;
|
|
128
|
+
vars=catx(' ',vars,_name_);
|
|
129
|
+
if col1=&rows then do;
|
|
130
|
+
pkguesses=_name_;
|
|
131
|
+
output;
|
|
132
|
+
end;
|
|
133
|
+
if last then call symputx('posspks',vars,'l');
|
|
134
|
+
run;
|
|
135
|
+
|
|
136
|
+
%if %mf_nobs(&outds) ge &max_guesses %then %do;
|
|
137
|
+
%put &sysmacroname: %mf_nobs(&outds) possible primary key values found;
|
|
138
|
+
%return;
|
|
139
|
+
%end;
|
|
140
|
+
|
|
141
|
+
%if &ppkcnt=1 %then %do;
|
|
142
|
+
%put &sysmacroname: No more PK guess possible;
|
|
143
|
+
%return;
|
|
144
|
+
%end;
|
|
145
|
+
|
|
146
|
+
/* begin scanning for uniques on pairs of PKs */
|
|
147
|
+
%let tmpds=%mf_getuniquename();
|
|
148
|
+
%local lev1 lev2;
|
|
149
|
+
%do i=1 %to &ppkcnt;
|
|
150
|
+
%let lev1=%scan(&posspks,&i);
|
|
151
|
+
%do j=2 %to &ppkcnt;
|
|
152
|
+
%let lev2=%scan(&posspks,&j);
|
|
153
|
+
%if &lev1 ne &lev2 %then %do;
|
|
154
|
+
/* check for two level uniqueness */
|
|
155
|
+
proc sort data=&pkds(keep=&lev1 &lev2) out=&tmpds noduprec;
|
|
156
|
+
by _all_;
|
|
157
|
+
run;
|
|
158
|
+
%if %mf_nobs(&tmpds)=&rows %then %do;
|
|
159
|
+
proc sql;
|
|
160
|
+
insert into &outds values("&lev1 &lev2");
|
|
161
|
+
%if %mf_nobs(&outds) ge &max_guesses %then %do;
|
|
162
|
+
%put &sysmacroname: Max PKs reached at Level 2 for &baseds;
|
|
163
|
+
%goto exit;
|
|
164
|
+
%end;
|
|
165
|
+
%end;
|
|
166
|
+
%end;
|
|
167
|
+
%end;
|
|
168
|
+
%end;
|
|
169
|
+
|
|
170
|
+
%if &ppkcnt=2 %then %do;
|
|
171
|
+
%put &sysmacroname: No more PK guess possible;
|
|
172
|
+
%goto exit;
|
|
173
|
+
%end;
|
|
174
|
+
|
|
175
|
+
/* begin scanning for uniques on PK triplets */
|
|
176
|
+
%local lev3;
|
|
177
|
+
%do i=1 %to &ppkcnt;
|
|
178
|
+
%let lev1=%scan(&posspks,&i);
|
|
179
|
+
%do j=2 %to &ppkcnt;
|
|
180
|
+
%let lev2=%scan(&posspks,&j);
|
|
181
|
+
%if &lev1 ne &lev2 %then %do k=3 %to &ppkcnt;
|
|
182
|
+
%let lev3=%scan(&posspks,&k);
|
|
183
|
+
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do;
|
|
184
|
+
/* check for three level uniqueness */
|
|
185
|
+
proc sort data=&pkds(keep=&lev1 &lev2 &lev3) out=&tmpds noduprec;
|
|
186
|
+
by _all_;
|
|
187
|
+
run;
|
|
188
|
+
%if %mf_nobs(&tmpds)=&rows %then %do;
|
|
189
|
+
proc sql;
|
|
190
|
+
insert into &outds values("&lev1 &lev2 &lev3");
|
|
191
|
+
%if %mf_nobs(&outds) ge &max_guesses %then %do;
|
|
192
|
+
%put &sysmacroname: Max PKs reached at Level 3 for &baseds;
|
|
193
|
+
%goto exit;
|
|
194
|
+
%end;
|
|
195
|
+
%end;
|
|
196
|
+
%end;
|
|
197
|
+
%end;
|
|
198
|
+
%end;
|
|
199
|
+
%end;
|
|
200
|
+
|
|
201
|
+
%if &ppkcnt=3 %then %do;
|
|
202
|
+
%put &sysmacroname: No more PK guess possible;
|
|
203
|
+
%goto exit;
|
|
204
|
+
%end;
|
|
205
|
+
|
|
206
|
+
/* scan for uniques on up to 4 PK fields */
|
|
207
|
+
%local lev4;
|
|
208
|
+
%do i=1 %to &ppkcnt;
|
|
209
|
+
%let lev1=%scan(&posspks,&i);
|
|
210
|
+
%do j=2 %to &ppkcnt;
|
|
211
|
+
%let lev2=%scan(&posspks,&j);
|
|
212
|
+
%if &lev1 ne &lev2 %then %do k=3 %to &ppkcnt;
|
|
213
|
+
%let lev3=%scan(&posspks,&k);
|
|
214
|
+
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
|
|
215
|
+
%let lev4=%scan(&posspks,&l);
|
|
216
|
+
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then %do;
|
|
217
|
+
/* check for four level uniqueness */
|
|
218
|
+
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4)
|
|
219
|
+
out=&tmpds noduprec;
|
|
220
|
+
by _all_;
|
|
221
|
+
run;
|
|
222
|
+
%if %mf_nobs(&tmpds)=&rows %then %do;
|
|
223
|
+
proc sql;
|
|
224
|
+
insert into &outds values("&lev1 &lev2 &lev3 &lev4");
|
|
225
|
+
%if %mf_nobs(&outds) ge &max_guesses %then %do;
|
|
226
|
+
%put &sysmacroname: Max PKs reached at Level 4 for &baseds;
|
|
227
|
+
%goto exit;
|
|
228
|
+
%end;
|
|
229
|
+
%end;
|
|
230
|
+
%end;
|
|
231
|
+
%end;
|
|
232
|
+
%end;
|
|
233
|
+
%end;
|
|
234
|
+
%end;
|
|
235
|
+
|
|
236
|
+
%if &ppkcnt=4 %then %do;
|
|
237
|
+
%put &sysmacroname: No more PK guess possible;
|
|
238
|
+
%goto exit;
|
|
239
|
+
%end;
|
|
240
|
+
|
|
241
|
+
/* scan for uniques on up to 4 PK fields */
|
|
242
|
+
%local lev5 m;
|
|
243
|
+
%do i=1 %to &ppkcnt;
|
|
244
|
+
%let lev1=%scan(&posspks,&i);
|
|
245
|
+
%do j=2 %to &ppkcnt;
|
|
246
|
+
%let lev2=%scan(&posspks,&j);
|
|
247
|
+
%if &lev1 ne &lev2 %then %do k=3 %to &ppkcnt;
|
|
248
|
+
%let lev3=%scan(&posspks,&k);
|
|
249
|
+
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
|
|
250
|
+
%let lev4=%scan(&posspks,&l);
|
|
251
|
+
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
|
|
252
|
+
%do m=5 %to &ppkcnt;
|
|
253
|
+
%let lev5=%scan(&posspks,&m);
|
|
254
|
+
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then %do;
|
|
255
|
+
/* check for four level uniqueness */
|
|
256
|
+
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4 &lev5)
|
|
257
|
+
out=&tmpds noduprec;
|
|
258
|
+
by _all_;
|
|
259
|
+
run;
|
|
260
|
+
%if %mf_nobs(&tmpds)=&rows %then %do;
|
|
261
|
+
proc sql;
|
|
262
|
+
insert into &outds values("&lev1 &lev2 &lev3 &lev4 &lev5");
|
|
263
|
+
%if %mf_nobs(&outds) ge &max_guesses %then %do;
|
|
264
|
+
%put &sysmacroname: Max PKs reached at Level 5 for &baseds;
|
|
265
|
+
%goto exit;
|
|
266
|
+
%end;
|
|
267
|
+
%end;
|
|
268
|
+
%end;
|
|
269
|
+
%end;
|
|
270
|
+
%end;
|
|
271
|
+
%end;
|
|
272
|
+
%end;
|
|
273
|
+
%end;
|
|
274
|
+
|
|
275
|
+
%if &ppkcnt=5 %then %do;
|
|
276
|
+
%put &sysmacroname: No more PK guess possible;
|
|
277
|
+
%goto exit;
|
|
278
|
+
%end;
|
|
279
|
+
|
|
280
|
+
/* scan for uniques on up to 4 PK fields */
|
|
281
|
+
%local lev6 n;
|
|
282
|
+
%do i=1 %to &ppkcnt;
|
|
283
|
+
%let lev1=%scan(&posspks,&i);
|
|
284
|
+
%do j=2 %to &ppkcnt;
|
|
285
|
+
%let lev2=%scan(&posspks,&j);
|
|
286
|
+
%if &lev1 ne &lev2 %then %do k=3 %to &ppkcnt;
|
|
287
|
+
%let lev3=%scan(&posspks,&k);
|
|
288
|
+
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
|
|
289
|
+
%let lev4=%scan(&posspks,&l);
|
|
290
|
+
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
|
|
291
|
+
%do m=5 %to &ppkcnt;
|
|
292
|
+
%let lev5=%scan(&posspks,&m);
|
|
293
|
+
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5
|
|
294
|
+
%then %do n=6 %to &ppkcnt;
|
|
295
|
+
%let lev6=%scan(&posspks,&n);
|
|
296
|
+
%if &lev1 ne &lev6 & &lev2 ne &lev6 & &lev3 ne &lev6
|
|
297
|
+
& &lev4 ne &lev6 & &lev5 ne &lev6 %then
|
|
298
|
+
%do;
|
|
299
|
+
/* check for four level uniqueness */
|
|
300
|
+
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4 &lev5 &lev6)
|
|
301
|
+
out=&tmpds noduprec;
|
|
302
|
+
by _all_;
|
|
303
|
+
run;
|
|
304
|
+
%if %mf_nobs(&tmpds)=&rows %then %do;
|
|
305
|
+
proc sql;
|
|
306
|
+
insert into &outds
|
|
307
|
+
values("&lev1 &lev2 &lev3 &lev4 &lev5 &lev6");
|
|
308
|
+
%if %mf_nobs(&outds) ge &max_guesses %then %do;
|
|
309
|
+
%put &sysmacroname: Max PKs reached at Level 6 for &baseds;
|
|
310
|
+
%goto exit;
|
|
311
|
+
%end;
|
|
312
|
+
%end;
|
|
313
|
+
%end;
|
|
314
|
+
%end;
|
|
315
|
+
%end;
|
|
316
|
+
%end;
|
|
317
|
+
%end;
|
|
318
|
+
%end;
|
|
319
|
+
%end;
|
|
320
|
+
|
|
321
|
+
%if &ppkcnt=6 %then %do;
|
|
322
|
+
%put &sysmacroname: No more PK guess possible;
|
|
323
|
+
%goto exit;
|
|
324
|
+
%end;
|
|
325
|
+
|
|
326
|
+
%exit:
|
|
327
|
+
%if &mdebug=0 %then %do;
|
|
328
|
+
proc sql;
|
|
329
|
+
drop table &tmpds;
|
|
330
|
+
%end;
|
|
331
|
+
|
|
332
|
+
%mend mp_guesspk;
|