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