diff --git a/all.sas b/all.sas
index d18573d..531a9ab 100644
--- a/all.sas
+++ b/all.sas
@@ -2216,6 +2216,14 @@ Usage:
select count(*) into: orig from &lib..&ds;
quit;
+ %local notfound;
+ proc sql outobs=10 noprint;
+ select distinct &col into: notfound separated by ' '
+ from &lib..&ds
+ where &col not in (
+ select &ccol from &clib..&cds
+ );
+
%mp_abort(iftrue= (&syscc ne 0)
,mac=&sysmacroname
,msg=%str(syscc=&syscc after macro query)
@@ -2226,7 +2234,7 @@ Usage:
test_description=symget('desc');
test_result='FAIL';
test_comments="&sysmacroname: &lib..&ds..&col has &result values "
- !!"not in &clib..&cds..&ccol ";
+ !!"not in &clib..&cds..&ccol.. First 10 vals:"!!symget('notfound');
%if &test=ANYVAL %then %do;
if &result < &orig then test_result='PASS';
%end;
@@ -4230,6 +4238,72 @@ filename &fref1 clear;
%mend mp_filtervalidate;
/**
+ @file
+ @brief Creates a dataset with column metadata.
+ @details This macro takes the `proc contents` output and "tidies it up" in the
+ following ways:
+
+ @li Blank labels are filled in with column names
+ @li Formats are reconstructed with default values
+ @li Types such as DATE / TIME / DATETIME are inferred from the formats
+
+ Example usage:
+
+ %mp_getcols(sashelp.airline,outds=work.myds)
+
+ @param ds The dataset from which to obtain column metadata
+ @param outds= (work.cols) The output dataset to create. Sample data:
+ |NAME $|LENGTH 8|VARNUM 8|LABEL $|FORMAT $49|TYPE $1 |DDTYPE $|
+ |---|---|---|---|---|---|---|
+ |AIR|8|2|international airline travel (thousands)|8.|N|NUMERIC|
+ |DATE|8|1|DATE|MONYY.|N|DATE|
+ |REGION|3|3|REGION|$3.|C|CHARACTER|
+
+
Related Macros
+ @li mf_getvarlist.sas
+ @li mm_getcols.sas
+
+ @version 9.2
+ @author Allan Bowe
+ @copyright Macro People Ltd - this is a licensed product and
+ NOT FOR RESALE OR DISTRIBUTION.
+
+**/
+
+%macro mp_getcols(ds, outds=work.cols);
+
+proc contents noprint data=&ds
+ out=_data_ (keep=name type length label varnum format:);
+run;
+data &outds(keep=name type length varnum format label ddtype);
+ set &syslast(rename=(format=format2 type=type2));
+ name=upcase(name);
+ if type2=2 then do;
+ length format $49.;
+ if format2='' then format=cats('$',length,'.');
+ else if formatl=0 then format=cats(format2,'.');
+ else format=cats(format2,formatl,'.');
+ type='C';
+ ddtype='CHARACTER';
+ end;
+ else do;
+ if format2='' then format=cats(length,'.');
+ else if formatl=0 then format=cats(format2,'.');
+ else if formatd=0 then format=cats(format2,formatl,'.');
+ else format=cats(format2,formatl,'.',formatd);
+ type='N';
+ if format=:'DATETIME' then ddtype='DATETIME';
+ else if format=:'DATE' or format=:'DDMMYY' or format=:'MMDDYY'
+ or format=:'YYMMDD' or format=:'E8601DA' or format=:'B8601DA'
+ or format=:'MONYY'
+ then ddtype='DATE';
+ else if format=:'TIME' then ddtype='TIME';
+ else ddtype='NUMERIC';
+ end;
+ if label='' then label=name;
+run;
+
+%mend mp_getcols;/**
@file mp_getconstraints.sas
@brief Get constraint details at column level
@details Useful for capturing constraints before they are dropped / reapplied
diff --git a/base/mp_assertcolvals.sas b/base/mp_assertcolvals.sas
index c10ea3a..5ee518c 100644
--- a/base/mp_assertcolvals.sas
+++ b/base/mp_assertcolvals.sas
@@ -115,6 +115,14 @@
select count(*) into: orig from &lib..&ds;
quit;
+ %local notfound;
+ proc sql outobs=10 noprint;
+ select distinct &col into: notfound separated by ' '
+ from &lib..&ds
+ where &col not in (
+ select &ccol from &clib..&cds
+ );
+
%mp_abort(iftrue= (&syscc ne 0)
,mac=&sysmacroname
,msg=%str(syscc=&syscc after macro query)
@@ -125,7 +133,7 @@
test_description=symget('desc');
test_result='FAIL';
test_comments="&sysmacroname: &lib..&ds..&col has &result values "
- !!"not in &clib..&cds..&ccol ";
+ !!"not in &clib..&cds..&ccol.. First 10 vals:"!!symget('notfound');
%if &test=ANYVAL %then %do;
if &result < &orig then test_result='PASS';
%end;
diff --git a/base/mp_getcols.sas b/base/mp_getcols.sas
new file mode 100644
index 0000000..8c5f315
--- /dev/null
+++ b/base/mp_getcols.sas
@@ -0,0 +1,67 @@
+/**
+ @file
+ @brief Creates a dataset with column metadata.
+ @details This macro takes the `proc contents` output and "tidies it up" in the
+ following ways:
+
+ @li Blank labels are filled in with column names
+ @li Formats are reconstructed with default values
+ @li Types such as DATE / TIME / DATETIME are inferred from the formats
+
+ Example usage:
+
+ %mp_getcols(sashelp.airline,outds=work.myds)
+
+ @param ds The dataset from which to obtain column metadata
+ @param outds= (work.cols) The output dataset to create. Sample data:
+ |NAME $|LENGTH 8|VARNUM 8|LABEL $|FORMAT $49|TYPE $1 |DDTYPE $|
+ |---|---|---|---|---|---|---|
+ |AIR|8|2|international airline travel (thousands)|8.|N|NUMERIC|
+ |DATE|8|1|DATE|MONYY.|N|DATE|
+ |REGION|3|3|REGION|$3.|C|CHARACTER|
+
+ Related Macros
+ @li mf_getvarlist.sas
+ @li mm_getcols.sas
+
+ @version 9.2
+ @author Allan Bowe
+ @copyright Macro People Ltd - this is a licensed product and
+ NOT FOR RESALE OR DISTRIBUTION.
+
+**/
+
+%macro mp_getcols(ds, outds=work.cols);
+
+proc contents noprint data=&ds
+ out=_data_ (keep=name type length label varnum format:);
+run;
+data &outds(keep=name type length varnum format label ddtype);
+ set &syslast(rename=(format=format2 type=type2));
+ name=upcase(name);
+ if type2=2 then do;
+ length format $49.;
+ if format2='' then format=cats('$',length,'.');
+ else if formatl=0 then format=cats(format2,'.');
+ else format=cats(format2,formatl,'.');
+ type='C';
+ ddtype='CHARACTER';
+ end;
+ else do;
+ if format2='' then format=cats(length,'.');
+ else if formatl=0 then format=cats(format2,'.');
+ else if formatd=0 then format=cats(format2,formatl,'.');
+ else format=cats(format2,formatl,'.',formatd);
+ type='N';
+ if format=:'DATETIME' then ddtype='DATETIME';
+ else if format=:'DATE' or format=:'DDMMYY' or format=:'MMDDYY'
+ or format=:'YYMMDD' or format=:'E8601DA' or format=:'B8601DA'
+ or format=:'MONYY'
+ then ddtype='DATE';
+ else if format=:'TIME' then ddtype='TIME';
+ else ddtype='NUMERIC';
+ end;
+ if label='' then label=name;
+run;
+
+%mend mp_getcols;
\ No newline at end of file
diff --git a/tests/crossplatform/mp_getcols.test.sas b/tests/crossplatform/mp_getcols.test.sas
new file mode 100644
index 0000000..eb0ef7d
--- /dev/null
+++ b/tests/crossplatform/mp_getcols.test.sas
@@ -0,0 +1,33 @@
+/**
+ @file
+ @brief Testing mp_getcols macro
+
+ SAS Macros
+ @li mp_getcols.sas
+ @li mp_assertcolvals.sas
+ @li mp_assertdsobs.sas
+
+**/
+
+
+/* valid filter */
+%mp_getcols(sashelp.airline,outds=work.info)
+
+
+%mp_assertdsobs(work.info,
+ desc=Has 3 records,
+ test=EQUALS 3,
+ outds=work.test_results
+)
+
+data work.check;
+ length val $10;
+ do val='NUMERIC','DATE','CHARACTER';
+ output;
+ end;
+run;
+%mp_assertcolvals(work.info.ddtype,
+ checkvals=work.check.val,
+ desc=All values have a match,
+ test=ALLVALS
+)
\ No newline at end of file