Macros for SAS Application Developers
https://github.com/sasjs/core
Loading...
Searching...
No Matches
mp_getpk.sas
Go to the documentation of this file.
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 @li mf_getuniquename.sas
43
44 <h4> Related Macros </h4>
45 @li mp_getpk.test.sas
46 @li mp_guesspk.sas
47
48 @version 9.3
49 @author Macro People Ltd
50 @sasjslint {"maxLineLength": 118}
51**/
52
53%macro mp_getpk(
54 lib,
55 ds=0,
56 outds=work.mp_getpk,
57 mdebug=0
58)/*/STORE SOURCE*/;
59
60
61%local engine schema ds1 ds2 ds3 dsn tabs1 tabs2 sum pk4sure pkdefault finalpks
62 pkfromindex;
63
64%let lib=%upcase(&lib);
65%let ds=%upcase(&ds);
66%let engine=%mf_getengine(&lib);
67%let schema=%mf_getschema(&lib);
68
69%let ds1=%mf_getuniquename(prefix=getpk_ds1);
70%let ds2=%mf_getuniquename(prefix=getpk_ds2);
71%let ds3=%mf_getuniquename(prefix=getpk_ds3);
72%let tabs1=%mf_getuniquename(prefix=getpk_tabs1);
73%let tabs2=%mf_getuniquename(prefix=getpk_tabs2);
74%let sum=%mf_getuniquename(prefix=getpk_sum);
75%let pk4sure=%mf_getuniquename(prefix=getpk_pk4sure);
76%let pkdefault=%mf_getuniquename(prefix=getpk_pkdefault);
77%let pkfromindex=%mf_getuniquename(prefix=getpk_pkfromindex);
78%let finalpks=%mf_getuniquename(prefix=getpk_finalpks);
79
80%local dbg;
81%if &mdebug=1 %then %do;
82 %put &sysmacroname entry vars:;
83 %put _local_;
84%end;
85%else %let dbg=*;
86
87proc sql;
88create table &ds1 as
89 select libname as libref
90 ,upcase(memname) as dsn
91 ,memtype
92 ,upcase(name) as name
93 ,type
94 ,length
95 ,varnum
96 ,label
97 ,format
98 ,idxusage
99 ,notnull
100 from dictionary.columns
101 where upcase(libname)="&lib"
102%if &ds ne 0 %then %do;
103 and upcase(memname)="&ds"
104%end;
105 ;
106
107
108%if &engine=SQLSVR %then %do;
109 proc sql;
110 connect using &lib;
111 create table work.&ds2 as
112 select * from connection to &lib(
113 select
114 s.name as SchemaName,
115 t.name as memname,
116 tc.name as name,
117 ic.key_ordinal as KeyOrderNr
118 from
119 sys.schemas s
120 inner join sys.tables t on s.schema_id=t.schema_id
121 inner join sys.indexes i on t.object_id=i.object_id
122 inner join sys.index_columns ic on i.object_id=ic.object_id
123 and i.index_id=ic.index_id
124 inner join sys.columns tc on ic.object_id=tc.object_id
125 and ic.column_id=tc.column_id
126 where i.is_primary_key=1
127 and s.name=%str(%')&schema%str(%')
128 order by t.name, ic.key_ordinal ;
129 );disconnect from &lib;
130 create table &ds3 as
131 select a.*
132 ,case when b.name is not null then 1 else 0 end as pk_ind
133 from work.&ds1 a
134 left join work.&ds2 b
135 on a.dsn=b.memname
136 and upcase(a.name)=upcase(b.name)
137 order by libref,dsn;
138%end;
139%else %do;
140
141 %if &ds = 0 %then %let dsn=;
142
143 /* get all constraints, in constraint order*/
144 %mp_getconstraints(lib=&lib,ds=&dsn,outds=work.&ds2)
145
146 /* extract cols that are clearly primary keys */
147 proc sql;
148 create table &pk4sure as
149 select libref
150 ,table_name
151 ,constraint_name
152 ,constraint_order
153 ,column_name as name
154 from work.&ds2
155 where constraint_type='PRIMARY'
156 order by 1,2,3,4;
157
158 /* extract unique constraints where every col is also NOT NULL */
159 proc sql;
160 create table &sum as
161 select a.libref
162 ,a.table_name
163 ,a.constraint_name
164 ,count(a.column_name) as unq_cnt
165 ,count(b.column_name) as nul_cnt
166 from work.&ds2(where=(constraint_type ='UNIQUE')) a
167 left join work.&ds2(where=(constraint_type ='NOT NULL')) b
168 on a.libref=b.libref
169 and a.table_name=b.table_name
170 and a.column_name=b.column_name
171 group by 1,2,3
172 having unq_cnt=nul_cnt;
173
174 /* extract cols from the relevant unique constraints */
175 create table &pkdefault as
176 select a.libref
177 ,a.table_name
178 ,a.constraint_name
179 ,b.constraint_order
180 ,b.column_name as name
181 from &sum a
182 left join &ds2(where=(constraint_type ='UNIQUE')) b
183 on a.libref=b.libref
184 and a.table_name=b.table_name
185 and a.constraint_name=b.constraint_name
186 order by 1,2,3,4;
187
188 /* extract cols from the relevant unique INDEXES */
189 create table &pkfromindex as
190 select libname as libref
191 ,memname as table_name
192 ,indxname as constraint_name
193 ,indxpos as constraint_order
194 ,name
195 from dictionary.indexes
196 where nomiss='yes' and unique='yes' and upcase(libname)="&lib"
197 %if &ds ne 0 %then %do;
198 and upcase(memname)="&ds"
199 %end;
200 order by 1,2,3,4;
201
202 /* create one table */
203 data &finalpks;
204 set &pkdefault &pk4sure &pkfromindex;
205 pk_ind=1;
206 /* if there are multiple unique constraints, take the first */
207 by libref table_name constraint_name;
208 retain keepme;
209 if first.table_name then keepme=1;
210 if first.constraint_name and not first.table_name then keepme=0;
211 if keepme=1;
212 run;
213
214 /* join back to starting table */
215 proc sql;
216 create table &ds3 as
217 select a.*
218 ,b.constraint_order
219 ,case when b.pk_ind=1 then 1 else 0 end as pk_ind
220 from work.&ds1 a
221 left join work.&finalpks b
222 on a.libref=b.libref
223 and a.dsn=b.table_name
224 and upcase(a.name)=upcase(b.name)
225 order by libref,dsn,constraint_order;
226%end;
227
228
229/* prepare tables */
230proc sql;
231create table work.&tabs1 as select
232 libname as libref
233 ,upcase(memname) as dsn
234 ,memtype
235%if %mf_existfeature(DBMS_MEMTYPE)=1 %then %do;
236 ,dbms_memtype
237%end;
238%else %do;
239 ,'n/a' as dbms_memtype format=$32.
240%end;
241 ,typemem
242 ,memlabel
243 ,nvar
244 ,compress
245from dictionary.tables
246 where upcase(libname)="&lib"
247%if &ds ne 0 %then %do;
248 and upcase(memname)="&ds"
249%end;
250 ;
251data &tabs2;
252 set &ds3;
253 length pk_fields $512;
254 retain pk_fields;
255 by libref dsn constraint_order;
256 if first.dsn then pk_fields='';
257 if pk_ind=1 then pk_fields=catx(' ',pk_fields,name);
258 if last.dsn then output;
259run;
260
261proc sql;
262create table &outds as
263 select a.libref
264 ,a.dsn
265 ,a.memtype
266 ,a.dbms_memtype
267 ,a.typemem
268 ,a.memlabel
269 ,a.nvar
270 ,a.compress
271 ,b.pk_fields
272 from work.&tabs1 a
273 left join work.&tabs2 b
274 on a.libref=b.libref
275 and a.dsn=b.dsn;
276
277/* tidy up */
278%mp_dropmembers(
279 &ds1 &ds2 &ds3 &dsn &tabs1 &tabs2 &sum &pk4sure &pkdefault &finalpks,
280 iftrue=(&mdebug=0)
281)
282
283%mend mp_getpk;