Macros for SAS Application Developers
https://github.com/sasjs/core
Loading...
Searching...
No Matches
mp_ds2ddl.sas
Go to the documentation of this file.
1/**
2 @file mp_ds2ddl.sas
3 @brief Extract DDL in various formats, by table or library
4 @details Data Definition Language relates to a set of SQL instructions used
5 to create tables in SAS or a database. The macro can be used at table or
6 library level. The default behaviour is to create DDL in SAS format.
7
8 Note - views are not currently supported.
9
10 Usage:
11
12 data test(index=(pk=(x y)/unique /nomiss));
13 x=1;
14 y='blah';
15 label x='blah';
16 run;
17 proc sql; describe table &syslast;
18 %mp_ds2ddl(work,test,flavour=tsql,showlog=YES)
19
20 <h4> SAS Macros </h4>
21 @li mf_existfileref.sas
22 @li mf_getvarcount.sas
23 @li mp_getconstraints.sas
24
25 @param [in] libref Libref of the library to create DDL for. Should already
26 be assigned.
27 @param [in] ds dataset to create ddl for (optional)
28 @param [in] fref= (getddl) the fileref to which to _append_ the DDL. If it
29 does not exist, it will be created.
30 @param [in] flavour= (SAS) The type of DDL to create. Options:
31 @li SAS
32 @li TSQL
33 @li PGSQL
34
35 @param [in]showlog= (YES) Set to NO to prevent the DDL showing in the log
36 @param [in] schema= () Choose a preferred schema name (default is to use
37 actual schema, else libref)
38 @param [in] applydttm= (NO) For non SAS DDL, choose if columns are created
39 with native datetime2 format or regular decimal type
40
41 @version 9.3
42 @author Allan Bowe
43**/
44
45%macro mp_ds2ddl(libref,ds,fref=getddl,flavour=SAS,showlog=YES,schema=
46 ,applydttm=NO
47)/*/STORE SOURCE*/;
48
49/* check fileref is assigned */
50%if %mf_existfileref(&fref)=0 %then %do;
51 filename &fref temp ;
52%end;
53
54%if %length(&libref)=0 %then %let libref=WORK;
55%let flavour=%upcase(&flavour);
56
57proc sql noprint;
58create table _data_ as
59 select * from dictionary.tables
60 where upcase(libname)="%upcase(&libref)"
61 and memtype='DATA' /* views not currently supported */
62 %if %length(&ds)>0 %then %do;
63 and upcase(memname)="%upcase(&ds)"
64 %end;
65 ;
66%local tabinfo; %let tabinfo=&syslast;
67
68create table _data_ as
69 select * from dictionary.columns
70 where upcase(libname)="%upcase(&libref)"
71 %if %length(&ds)>0 %then %do;
72 and upcase(memname)="%upcase(&ds)"
73 %end;
74 ;
75%local colinfo; %let colinfo=&syslast;
76
77%local dsnlist;
78 select distinct upcase(memname) into: dsnlist
79 separated by ' '
80 from &syslast
81;
82
83create table _data_ as
84 select * from dictionary.indexes
85 where upcase(libname)="%upcase(&libref)"
86 %if %length(&ds)>0 %then %do;
87 and upcase(memname)="%upcase(&ds)"
88 %end;
89 order by idxusage, indxname, indxpos
90 ;
91%local idxinfo; %let idxinfo=&syslast;
92
93/* Extract all Primary Key and Unique data constraints */
94%mp_getconstraints(lib=%upcase(&libref),ds=%upcase(&ds),outds=_data_)
95%local colconst; %let colconst=&syslast;
96
97%local constraints_used;
98%macro addConst();
99 data _null_;
100 length ctype $11 constraint_name_orig $256 constraints_used $5000;
101 set &colconst(
102 where=(table_name="&curds" and constraint_type in ('PRIMARY','UNIQUE'))
103 ) end=last;
104 file &fref mod;
105 by constraint_type constraint_name;
106 retain constraints_used;
107 constraint_name_orig=constraint_name;
108 if upcase(strip(constraint_type)) = 'PRIMARY' then ctype='PRIMARY KEY';
109 else ctype=strip(constraint_type);
110 %if &flavour=TSQL %then %do;
111 column_name=catt('[',column_name,']');
112 constraint_name=catt('[',constraint_name,']');
113 %end;
114 %else %if &flavour=PGSQL %then %do;
115 column_name=catt('"',column_name,'"');
116 constraint_name=catt('"',constraint_name,'"');
117 %end;
118 if first.constraint_name then do;
119 constraints_used = catx(' ', constraints_used, constraint_name_orig);
120 put " ,CONSTRAINT " constraint_name ctype "(" ;
121 put ' ' column_name;
122 end;
123 else put ' ,' column_name;
124 if last.constraint_name then do;
125 put " )";
126 call symput('constraints_used',strip(constraints_used));
127 end;
128 run;
129 %put &=constraints_used;
130%mend addConst;
131
132data _null_;
133 file &fref mod;
134 put "/* DDL generated by &sysuserid on %sysfunc(datetime(),datetime19.) */";
135run;
136
137%local x curds;
138%if &flavour=SAS %then %do;
139 %do x=1 %to %sysfunc(countw(&dsnlist));
140 %let curds=%scan(&dsnlist,&x);
141 data _null_;
142 file &fref mod;
143 put "/* SAS Flavour DDL for %upcase(&libref).&curds */";
144 put "proc sql;";
145 run;
146 data _null_;
147 file &fref mod;
148 length lab $1024 typ $20;
149 set &colinfo (where=(upcase(memname)="&curds")) end=last;
150
151 if _n_=1 then do;
152 if memtype='DATA' then do;
153 put "create table &libref..&curds(";
154 end;
155 else do;
156 /* just a placeholder - we filter out views at the top */
157 put "create view &libref..&curds(";
158 end;
159 put " "@@;
160 end;
161 else put " ,"@@;
162 if length(format)>1 then fmt=" format="!!cats(format);
163 if length(label)>1 then
164 lab=" label="!!cats("'",tranwrd(label,"'","''"),"'");
165 if notnull='yes' then notnul=' not null';
166 if type='char' then typ=cats('char(',length,')');
167 else if length ne 8 then typ='num length='!!cats(length);
168 else typ='num';
169 put name typ fmt notnul lab;
170 run;
171
172 /* Extra step for data constraints */
173 %addConst()
174
175 data _null_;
176 file &fref mod;
177 put ');';
178 run;
179
180 /* Create Unique Indexes, but only if they were not already defined within
181 the Constraints section. */
182 data _null_;
183 *length ds $128;
184 set &idxinfo(
185 where=(
186 memname="&curds"
187 and unique='yes'
188 and indxname not in (
189 %sysfunc(tranwrd("&constraints_used",%str( ),%str(",")))
190 )
191 )
192 );
193 file &fref mod;
194 by idxusage indxname;
195/* ds=cats(libname,'.',memname); */
196 if first.indxname then do;
197 put 'CREATE UNIQUE INDEX ' indxname "ON &libref..&curds (" ;
198 put ' ' name ;
199 end;
200 else put ' ,' name ;
201 *else put ' ,' name ;
202 if last.indxname then do;
203 put ');';
204 end;
205 run;
206
207/*
208 ods output IntegrityConstraints=ic;
209 proc contents data=testali out2=info;
210 run;
211 */
212 %end;
213%end;
214%else %if &flavour=TSQL %then %do;
215 /* if schema does not exist, set to be same as libref */
216 %local schemaactual;
217 proc sql noprint;
218 select sysvalue into: schemaactual
219 from dictionary.libnames
220 where upcase(libname)="&libref" and engine='SQLSVR';
221 %let schema=%sysfunc(coalescec(&schemaactual,&schema,&libref));
222
223 %do x=1 %to %sysfunc(countw(&dsnlist));
224 %let curds=%scan(&dsnlist,&x);
225 data _null_;
226 file &fref mod;
227 put "/* TSQL Flavour DDL for &schema..&curds */";
228 data _null_;
229 file &fref mod;
230 set &colinfo (where=(upcase(memname)="&curds")) end=last;
231 if _n_=1 then do;
232 if memtype='DATA' then do;
233 put "create table [&schema].[&curds](";
234 end;
235 else do;
236 /* just a placeholder - we filter out views at the top */
237 put "create view [&schema].[&curds](";
238 end;
239 put " "@@;
240 end;
241 else put " ,"@@;
242 format=upcase(format);
243 if 1=0 then; /* dummy if */
244 %if &applydttm=YES %then %do;
245 else if format=:'DATETIME' then fmt='[datetime2](7) ';
246 %end;
247 else if type='num' then fmt='[decimal](18,2)';
248 else if length le 8000 then fmt='[varchar]('!!cats(length)!!')';
249 else fmt=cats('[varchar](max)');
250 if notnull='yes' then notnul=' NOT NULL';
251 put "[" name +(-1) "]" fmt notnul;
252 run;
253
254 /* Extra step for data constraints */
255 %addConst()
256
257 /* Create Unique Indexes, but only if they were not already defined within
258 the Constraints section. */
259 data _null_;
260 *length ds $128;
261 set &idxinfo(
262 where=(
263 memname="&curds"
264 and unique='yes'
265 and indxname not in (
266 %sysfunc(tranwrd("&constraints_used",%str( ),%str(",")))
267 )
268 )
269 );
270 file &fref mod;
271 by idxusage indxname;
272 *ds=cats(libname,'.',memname);
273 if first.indxname then do;
274 /* add nonclustered in case of multiple unique indexes */
275 put ' ,index [' indxname +(-1) '] UNIQUE NONCLUSTERED (';
276 put ' [' name +(-1) ']';
277 end;
278 else put ' ,[' name +(-1) ']';
279 if last.indxname then do;
280 put ' )';
281 end;
282 run;
283
284 data _null_;
285 file &fref mod;
286 put ')';
287 put 'GO';
288 run;
289
290 /* add extended properties for labels */
291 data _null_;
292 file &fref mod;
293 length nm $64 lab $1024;
294 set &colinfo (where=(upcase(memname)="&curds" and label ne '')) end=last;
295 nm=cats("N'",tranwrd(name,"'","''"),"'");
296 lab=cats("N'",tranwrd(label,"'","''"),"'");
297 put ' ';
298 put "EXEC sys.sp_addextendedproperty ";
299 put " @name=N'MS_Description',@value=" lab ;
300 put " ,@level0type=N'SCHEMA',@level0name=N'&schema' ";
301 put " ,@level1type=N'TABLE',@level1name=N'&curds'";
302 put " ,@level2type=N'COLUMN',@level2name=" nm ;
303 if last then put 'GO';
304 run;
305 %end;
306%end;
307%else %if &flavour=PGSQL %then %do;
308 /* if schema does not exist, set to be same as libref */
309 %local schemaactual;
310 proc sql noprint;
311 select sysvalue into: schemaactual
312 from dictionary.libnames
313 where upcase(libname)="&libref" and engine='POSTGRES';
314 %let schema=%sysfunc(coalescec(&schemaactual,&schema,&libref));
315 data _null_;
316 file &fref mod;
317 put "CREATE SCHEMA &schema;";
318 %do x=1 %to %sysfunc(countw(&dsnlist));
319 %let curds=%scan(&dsnlist,&x);
320 %local curdsvarcount;
321 %let curdsvarcount=%mf_getvarcount(&libref..&curds);
322 %if &curdsvarcount>1600 %then %do;
323 data _null_;
324 file &fref mod;
325 put "/* &libref..&curds contains &curdsvarcount vars */";
326 put "/* Postgres cannot create tables with over 1600 vars */";
327 put "/* No DDL will be generated for this table";
328 run;
329 %end;
330 %else %do;
331 data _null_;
332 file &fref mod;
333 put "/* Postgres Flavour DDL for &schema..&curds */";
334 data _null_;
335 file &fref mod;
336 set &colinfo (where=(upcase(memname)="&curds")) end=last;
337 length fmt $32;
338 if _n_=1 then do;
339 if memtype='DATA' then do;
340 put "CREATE TABLE &schema..&curds (";
341 end;
342 else do;
343 /* just a placeholder - we filter out views at the top */
344 put "CREATE VIEW &schema..&curds (";
345 end;
346 put " "@@;
347 end;
348 else put " ,"@@;
349 format=upcase(format);
350 if 1=0 then; /* dummy if */
351 %if &applydttm=YES %then %do;
352 else if format=:'DATETIME' then fmt=' TIMESTAMP ';
353 %end;
354 else if type='num' then fmt=' DOUBLE PRECISION';
355 else fmt='VARCHAR('!!cats(length)!!')';
356 if notnull='yes' then notnul=' NOT NULL';
357 /* quote column names in case they represent reserved words */
358 name2=quote(trim(name));
359 put name2 fmt notnul;
360 run;
361
362 /* Extra step for data constraints */
363 %addConst()
364
365 data _null_;
366 file &fref mod;
367 put ');';
368 run;
369
370 /* Create Unique Indexes, but only if they were not already defined within
371 the Constraints section. */
372 data _null_;
373 *length ds $128;
374 set &idxinfo(
375 where=(
376 memname="&curds"
377 and unique='yes'
378 and indxname not in (
379 %sysfunc(tranwrd("&constraints_used",%str( ),%str(",")))
380 )
381 )
382 );
383 file &fref mod;
384 by idxusage indxname;
385 if first.indxname then do;
386 put 'CREATE UNIQUE INDEX "' indxname +(-1) '" ' "ON &schema..&curds(";
387 put ' "' name +(-1) '"' ;
388 end;
389 else put ' ,"' name +(-1) '"';
390 if last.indxname then do;
391 put ');';
392 end;
393 run;
394 %end;
395 %end;
396%end;
397%if %upcase(&showlog)=YES %then %do;
398 options ps=max;
399 data _null_;
400 infile &fref;
401 input;
402 putlog _infile_;
403 run;
404%end;
405
406%mend mp_ds2ddl;