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