SAS table string length (limit) - sql

I am creating a SAS table in which one of the fields has to holds a huge string.
Following is my table (TABLE name = MKPLOGS4):
OBS RNID DESCTEXT
--------- -----------
1 123 This is some text which is part of the record. I want this to appear
2 123 concatenated kinda like concat_group() from MYSQL but for some reason
3 123 SAS does not have such functionality. Now I am having trouble with
4 123 String concatenation.
5 124 Hi there old friend of mine, hope you are enjoying the weather
6 124 Are you sure this is not your jacket, okay then. Will give charity.
. . .
. . .
. . .
and I have to get a Table similar to this (table name = MKPLOGSA):
OBS RNID DESCTEXT
--------- -----------
1 123 This is some text which is part of the record. I want this to appear concatenated kinda like concat_group() from MYSQL but for some reason SAS does not have such functionality. Now I am having trouble with String concatenation.
2 124 Hi, there old friend of mine, hope you are enjoying the weather Are you sure this is not your jacket, okay then. Will give charity.
. . .
. . .
. . .
So, after trying unsuccessfully with SQL, I came up with the following SAS code (please note I am very new at SAS):
DATA MKPLOGSA (DROP = DTEMP DTEXT);
SET MKPLOGS4;
BY RNID;
RETAIN DTEXT;
IF FIRST.RNID THEN
DO;
DTEXT = DESCTEXT;
DELETE;
END;
ELSE IF LAST.RNID THEN
DO;
DTEMP = CATX(' ',DTEXT,DESCTEXT);
DESCTEXT = DTEMP;
END;
ELSE
DO;
DTEMP = CATX(' ',DTEXT,DESCTEXT);
DTEXT = DTEMP;
DELETE;
END;
The SAS log is producing this warning message:
WARNING: IN A CALL TO THE CATX FUNCTION, THE BUFFER ALLOCATED
FOR THE RESULT WAS NOT LONG ENOUGH TO CONTAIN THE CONCATENATION
OF ALL THE ARGUMENTS. THE CORRECT RESULT WOULD CONTAIN 229 CHARACTERS,
BUT THE ACTUAL RESULT MAY EITHER BE TRUNCATED TO 200 CHARACTER(S) OR
BE COMPLETELY BLANK, DEPENDING ON THE CALLING ENVIRONMENT. THE
FOLLOWING NOTE INDICATES THE LEFT-MOST ARGUMENT THAT CAUSED TRUNCATION.
Followed by the message (for the SAS data step I posted here):
NOTE: ARGUMENT 3 TO FUNCTION CATX AT LINE 100 COLUMN 15 IS INVALID.
Please note that in my sample data table (MKPLOGS4), each line of string for the field DESCTEXT can be upto 116 characters and there is no limit as to how many lines of description text/recordID.
The output I am getting has only the last line of description:
OBS RNID DESCTEXT
---- --------
1 123 String concatenation.
2 124 Are you sure this is not your jacket, okay then. Will give charity.
. . .
. . .
. . .
I have the following questions:
. is there something wrong with my code?
. is there a limit to SAS string concatenation? Can I override this? If yes, please provide code.
If you have a suggestion, I would really appreciate if you can post your version of code. This is not school work/homework.

Since SAS stores character data as blank-padded fixed length strings, it is usually not a good idea to store a large amount of text in the dataset. However, if you must, then you can create a character type variable with a length of up to 32767 characters. If you don't mind doing some extra I/O, here is an easy way.
/* test data -- same id repeated over multiple observations i.e., in a "long-format" */
data one;
input rnid desctext & :$200.;
cards;
123 This is some text which is part of the record. I want this to appear
123 concatenated kinda like concat_group() from MYSQL but for some reason
123 SAS does not have such functionality. Now I am having trouble with
123 String concatenation.
124 Hi there old friend of mine, hope you are enjoying the weather
124 Are you sure this is not your jacket, okay then. Will give charity.
;
run;
/* re-shape from the long to the wide-format. assumes that data are sorted by rnid. */
proc transpose data=one out=two;
by rnid;
var desctext;
run;
/* concatenate col1, col2, ... vars into single desctext */
data two;
length rnid 8 desctext $1000;
keep rnid desctext;
set two;
desctext = catx(' ', of col:);
run;

The documentation for the catx function specifies that it will (by default) only return 200 characters unless you have already specified a length for the string you are storing the result to.
All you need to do is add either a length or an attrib statement somewhere in your datastep.
Here is how I would have coded it (untested):
data mkplogsa (rename=dtext=desctext);
length dtext $32767 ;
set mkplogs4;
by rnid;
retain dtext;
if first.rnid then do;
dtext = "";
end;
dtext = catx(' ',dtext,desctext);
if last.rnid then do;
output;
end;
keep dtext;
run;
Note that 32767 is the largest string size for a character value in a SAS dataset. If your string is larger than that you're out of luck.
Cheers
Rob

Thanks guys, I was able to solve this problem by using PROC TRANSPOSE and then using concatenation. Here is the code:
/*
THIS TRANSPOSE STEP TAKES THE MKPLOGS4 TABLE AND
CREATES A NEW TEMPORARY TABLE CALLED MKPLOGSA. SINCE
THE DESCRIPTION TEXT IS STORED IN MULTIPLE LINES (OBSERVATIONS)
IN THE ITEXT FILE, IN ORDER TO COMBINE THEM TO A SINGLE ROW,
WE USE TRANSPOSE. HOWEVER, AFTER THIS STEP, THE DESCRIPTION TEXT
SPREAD OVER MULTIPLE LINES ALTHOUGH ON SAME ROW (OBSERVATION)
ARE STILL SEPARATED INTO MULTIPLE COLUMNS (ON THE SAME ROW)
ALL PREFIXED IN THIS CASE BY 'DESCTEXT'. WE DROP THE AUTO-CREATED
COLUMN _NAME_
*/
PROC TRANSPOSE DATA = MKPLOGS4 OUT = MKPLOGSA (DROP = _NAME_)
PREFIX = DESCTEXT;
VAR DESCTEXT;
BY PLOG;
RUN;
/*
THIS DATA STEP CREATES A NEW TABLE CALLED MKPLOGSB WHICH
TAKES ALL THE SEPARATED DESCRIPTION TEXT COLUMNS AND
CONCATENATES THEM INTO A SINGLE COLUMN - LONG_DESCRIPTION.
*/
DATA MKPLOGSB (DROP = DESCTEXT:);
SET MKPLOGSA;
/* CONCATENATED DESC. TEXT SET TO MAX. 27000 CHARS. */
LENGTH LONG_DESCRIPTION $27000;
LONG_DESCRIPTION = CATX(' ',OF DESCTEXT:);
RUN;

Related

proc sql filter for values that end in a list of strings

I have an example table:
data data;
length code $30;
input code$;
datalines;
PPPES
PPPEW
pppESS
saf
xwq3
er32
ddES
ea9ESS
asesEo
ewlEa
;
run;
and I want to filter for rows that end in ES, ESS, or EW. I tried the following but it didn't work:
proc sql;
create table revised as
select *
from data
where code like ("%ES", "%ESS", "%EW")
quit;
Is there a way to filter if a variable ends in a possible list of string values?
This is my desired output:
data data1;
length code $30;
input code$;
datalines;
PPPES
PPPEW
pppESS
ddES
ea9ESS
;
run;
No.
Either explicitly test for each string.
where code like '%ES' or code like '%ESS' or code like '%EW'
In a data step you could use either of these:
if left(reverse(code)) in: ('SE','SSE','WE');
where left(reverse(code)) in: ('SE','SSE','WE');
PROC SQL does not support the truncated comparisons specified by the : modifier. But you could use the WHERE= dataset option
from data(where=(left(reverse(code)) in: ('SE','SSE','WE')))
Using "or" and simple quotation marks:
data data;
length code $30;
input code$;
datalines;
PPPES
PPPEW
pppESS
saf
xwq3
er32
ddES
ea9ESS
asesEo
ewlEa
;
run;
proc sql;
create table revised as
select *
from data
where code like ('%ES') or code like ('%ESS') or code like ('%EW');
quit;
In some scenarios you may want to cross join your search terms (as data) with your data, or do an existential test on your data.
data endings;
length target $10;
input target $char10.;
datalines;
ES
ESS
EW
;
data have;
length code $30;
input code $char30.;
datalines;
PPPES
PPPEW
pppESS
saf
xwq3
er32
ddES
ea9ESS
asesEo
ewlEa
;
run;
* cross join;
proc sql;
create table want as
select distinct code
from have
cross join endings
having code like '%'||target
;
quit;
* existential test;
proc sql;
create table want as
select distinct code from have
where exists (
select * from endings
where code like '%'||target
);
quit;
You might also need to deal with case insensitive searches by uppercasing the data values.

Foxpro String Variable combination in Forloop

As in title, there is an error in my first code in FOR loop: Command contains unrecognized phrase. I am thinking if the method string+variable is wrong.
ALTER TABLE table1 ADD COLUMN prod_n c(10)
ALTER TABLE table1 ADD COLUMN prm1 n(19,2)
ALTER TABLE table1 ADD COLUMN rbon1 n(19,2)
ALTER TABLE table1 ADD COLUMN total1 n(19,2)
There are prm2... until total5, in which the numbers represent the month.
FOR i=1 TO 5
REPLACE ALL prm+i WITH amount FOR LEFT(ALLTRIM(a),1)="P" AND
batch_mth = i
REPLACE ALL rbon+i WITH amount FOR LEFT(ALLTRIM(a),1)="R"
AND batch_mth = i
REPLACE ALL total+i WITH sum((prm+i)+(rbon+i)) FOR batch_mth = i
NEXT
ENDFOR
Thanks for the help.
There are a number of things wrong with the code you posted above. Cetin has mentioned a number of them, so I apologize if I duplicate some of them.
PROBLEM 1 - in your ALTER TABLE commands I do not see where you create fields prm2, prm3, prm4, prm5, rbon2, rbon3, etc.
And yet your FOR LOOP would be trying to write to those fields as the FOR LOOP expression i increases from 1 to 5 - if the other parts of your code was correct.
PROBLEM 2 - You cannot concatenate a String to an Integer so as to create a Field Name like you attempt to do with prm+i or rbon+1
Cetin's code suggestions would work (again as long as you had the #2, #3, etc. fields defined). However in Foxpro and Visual Foxpro you can generally do a task in a variety of ways.
Personally, for readability I'd approach your FOR LOOP like this:
FOR i=1 TO 5
* --- Keep in mind that unless fields #2, #3, #4, & #5 are defined ---
* --- The following will Fail ---
cFld1 = "prm" + STR(i,1) && define the 1st field
cFld2 = "rbon" + STR(i,1) && define the 2nd field
cFld3 = "total" + STR(i,1) && define the 3rd field
REPLACE ALL &cFld1 WITH amount ;
FOR LEFT(ALLTRIM(a),1)="P" AND batch_mth = i
REPLACE ALL &cFld2 WITH amount ;
FOR LEFT(ALLTRIM(a),1)="R" AND batch_mth = i
REPLACE ALL &cFld3 WITH sum((prm+i)+(rbon+i)) ;
FOR batch_mth = i
NEXT
NOTE - it might be good if you would learn to use VFP's Debug tools so that you can examine your code execution line-by-line in the VFP Development mode. And you can also use it to examine the variable values.
Breakpoints are good, but you have to already have the TRACE WINDOW open for the Break to work.
SET STEP ON is the Debug command that I generally use so that program execution will stop and AUTOMATICALLY open the TRACE WINDOW for looking at code execution and/or variable values.
Do you mean you have fields named prm1, prm2, prm3 ... prm12 that represent the months and you want to update them in a loop? If so, you need to understand that a "fieldName" is a "name" and thus you need to use a "name expression" to use it as a variable. That is:
prm+i
would NOT work but:
( 'pro'+ ltrim(str(m.i)) )
would.
For example here is your code revised:
For i=1 To 5
Replace All ('prm'+Ltrim(Str(m.i))) With amount For Left(Alltrim(a),1)="P" And batch_mth = m.i
Replace All ('rbon'+Ltrim(Str(m.i))) With amount For Left(Alltrim(a),1)="R" And batch_mth = m.i
* ????????? REPLACE ALL ('total'+Ltrim(Str(m.i))) WITH sum((prm+i)+(rbon+i)) FOR batch_mth = i
Endfor
However, I must admit, your code doesn't make sense to me. Maybe it would be better if you explained what you are trying to do and give some simple data with the result you expect (as code - you can use FAQ 50 on foxite to create code for data).

RPG IV Files processing issue

I am still learning RPG and AS/400. coming from MS C#.NET.
we are currently moving to iSeries machine and I am trying to pickup the IBM RPG and CL programming as it is more needed for the company.
the iSeries version if I am reading it right is
dspdtaara QSS1MRI: ==> "V7R1M000 2924"
DSPSFWRSC+ F11 : ==>
5770999 *BASE 5050 *CODE QSYS V7R1M0 L00
5770SS1 *BASE 5050 *CODE QSYS V7R1M0 L00
5770SS1 *BASE 2924 *LNG QSYS V7R1M0 L00
...
5770WDS 56 5101 *CODE QDEVTOOLS V7R1M0
5770WDS 60 5050 *CODE QDEVTOOLS V7R1M0
here is my issue.
I have a flat file that is generated on the old AS400 machine and is copied to the new iSeries
file is a delimited data (using ";" ) that I need to reformat and export to FTP for further processing by outside company. each record have either 29 or 28 columns. thus first thing I do is using an SQLRPGLE program add an extra delimiter to the records that are missing one. in the flat file itself
C/EXEC SQL
C+ SELECT
C+ MAX((length(trim(F00001))- length(REPLACE(trim(F00001),';',''))))
C+ , MIN((length(trim(F00001))- length(REPLACE(trim(F00001),';',''))))
C+ INTO :MaxCount, :#DelimCount
C+ FROM QGPL.fIncomming
C/END-EXEC
**/* using the delimiter could fix data by inserting a delimiter in the proper place */
C/EXEC SQL
C+ UPDATE QGPL.fIncomming
C+ SET F00001 = INSERT(F00001,225,0,';')
C+ WHERE ( :MaxCount
C+ - ( length(trim(F00001))
C+ - length(REPLACE(trim(F00001),';','')) )
C+ ) > 0
C/END-EXEC
then I created 2 tables (PF) on iSeries
Table1, which is an SQL table with 29 columns based on the incoming file format, (All columns are named and set to length of the data, but all columns are text (A) type )
and the second table (Table2) which have exactly the same layout as table (Table1) but each columns is of specific data type as needed.
i.e. id is INTEGER and dateof is DATE etc.
no the data is not clean. thus a date field might have spaces in it or money field might have text in it.
I need the best way to move the data to table2 cleaning up and validating it during the transfer.
the SQL way would be the best but the statement is so big I can not truly validate it properly.
can some one suggest (with an example if possible) other ways or better way to write SQL for it
I basically have this but get a lot of errors: and if I try to run it in STRSQL not all the code fits in a screen.
SELECT
TRIM(ORDERID) as ORDERID
...,LINENUM) <> ''
THEN TRIM(LINENUM)
ELSE 9999 END as LINENUM
, TRIM(CUSTNUM) as CUSTNUM
, TRIM(PONUM) as PONUM
, CASE WHEN TRIM(REPLACE(ORDDATE,0,'')) <> ''
THEN CAST(INSERT(INSERT(ORDDATE,5,0,'/'),3,0,'/') as Date)
...
from Table1
AS REQUESTED:
This is the original flat file data [preceded first by column headings c1-c29 for records with 29 effective columns of delimited data, then second by column headings c1-c28 with a *Err-> pointing out where the expected delimiter and 30 blanks are missing for the secondary records because each record should share the same layout, and third by an embedded ruler-line to show the 225th position where the semicolon would be added with the prior SQL code and optionally 30 blanks could be added to effect maintaining the fixed-length layout.]:
---c1----c2---c3----c4---------c5-------c6-------c7-------c8----------c9-----------c10-------c11--------------c12--------------c13----c14-c15---c16---c17---c18------c19---------c20------c21-----c22-------------c23-----------------------c24------------------c25----c26----c27------c28-----c29---
---c1----c2---c3----c4---------c5-------c6-------c7-------c8----------c9-----------c10-------c11--------------c12--------------c13----c14-c15---c16---c17---c18------c19---------c20------c21-----c22-------------c23-----*ERR->---c24--c25----c26-------c27-----c28-- **missing one column!**
....+....1....+....2....+....3....+....4....+....5....+....6....+....7....+....8....+....9....+...10....+...11....+...12....+...13....+...14....+...15....+...16....+...17....+...18....+...19....+...20....+...21....+...22....+...23....+...24....+...25....+...26....+...27....+...28....+...29....+
1596555;001;10010;TEST5 ;01062015; 1213.00; 1219.00; 17.000;NET 30 DAYS ; ;543534241;TOYYO1/5 ; 14OZ ;T; 5.00; .500; 1; 560.00; ; 560.00;01292015;5379602;** 2ND DAY ** ;5XDFSDFFGFGHGHGH16 ; ; ; ; ;
1596555; ;10010; ; ; ; ; ; ; ; ; ; ; ; ; . ; ; ; ; ;01292015;5379602; ; 16.60;FRT; ; ;
1598556;001;10021;TEST ;02112015; 1237.00; 1207.00; 17.000;NET 30 DAYS ; ;567860502;45GGH/4019 ; 10OZ ;R;12.50; .000; 1; 105.42; ; 105.42;02122015;5380313;** 2ND DAY ** ;3HGFH5456GFHFG5G27 ; ; ; ; ;
1598556; ;10021; ; ; ; ; ; ; ; ; ; ; ; ; . ; ; ; ; ;02122015;5380313; ; 13.19;FRT; ; ;
1598557;001;10067;020415 ;02042015; 1283.00; 1238.00; 18.000;NET 30 DAYS ; ;657870142;FTKG061/11 ; 14OZ ;R; ; .330; 1; 358.00; ; 358.00;02092015;5380071;** 2ND DAY ** ;3NHJYJ64646GHJGHJ8 ; ; ; ; ;
1598557; ;10067; ; ; ; ; ; ; ; ; ; ; ; ; . ; ; ; ; ;02092015;5380071; ; 15.09;FRT; ; ;
And this is the desired outcome that is currently generate by MS SQL process [preceded by an embedded ruler-line]:
....+....1....+....2....+....3....+....4....+....5....+....6....+....7....+....8....+....9....+...10....+...11....+...12....+...13....+...14....+...15....+...16....+...17....+...18....+...19....+...20....+...21....+
"1596555",1,"10010","TEST5",01/06/2015, 1213.00, 1219.00, 17.00,"NET 30 DAYS",,"543534241","TOYYO1/5","14OZ","T",5.00,0.50,1, 560.00,, 560.00,01/29/2015,"5379602","** 2ND DAY **","5XDFSDFFGFGHGHGH16",,,,,
"1596555",9999,"10010",,,,,,,,,,,,,,,,,,01/29/2015,"5379602",,,"16.60","FRT",,,
"1598556",1,"10021","TEST",02/11/2015, 1237.00, 1207.00, 17.00,"NET 30 DAYS",,"567860502","45GGH/4019","10OZ","R",12.50,0.00,1, 105.42,, 105.42,02/12/2015,"5380313","** 2ND DAY **","3HGFH5456GFHFG5G27",,,,,
"1598556",9999,"10021",,,,,,,,,,,,,,,,,,02/12/2015,"5380313",,,"13.19","FRT",,,
"1598557",1,"10067","020415",02/04/2015, 1283.00, 1238.00, 18.00,"NET 30 DAYS",,"657870142","FTKG061/11","14OZ","R",,0.33,1, 358.00,, 358.00,02/09/2015,"5380071","** 2ND DAY **","3NHJYJ64646GHJGHJ8",,,,,
"1598557",9999,"10067",,,,,,,,,,,,,,,,,,02/09/2015,"5380071",,,"15.09","FRT",,,
I would read the FIncomming directly into the RPG program, and not worry about that missing delimiter. I am guessing it is at the end of the record anyway (you didn't really tell us that). Parse out the fileds as you would in C/C++, the standard C library is available to RPGIV. For each record validate the fields, and if all are valid, write directly to Table 2 mentioned in your question. Records with errors would be written to Table 1 for remediation.
Now you mention that the data needs to be FTP'd to an outside company, that is another issue altogether. Scott Klement has an API that you can use to FTP files directly from within RPG. His web site is http://www.scottklement.com.
And one more thing, given that you are new to RPG, you might want to learn free format rather than the fixed format variant as that is the modern way to code RPG. That SQL statement in free format would look like:
exec sql
UPDATE qgpl.fIncomming
SET F00001 = INSERT(F00001,225,0,';')
WHERE :MaxCount - length(trim(F00001) - length(REPLACE(trim(F00001),';',''))) > 0;
There are many ways to generate a comma seperated file from a database file. The easiest is to use CL command CPYTOIMPF, prompt it, and you can see the options. That command can also drop the file directly into the IFS with translation to ASCII for transfer to MS-SQL.
Edit March 3, 2017
Wow, looks like some folks are using some really old stuff. So if you are on a release of i5/OS, or OS/400 prior to V5R4, the code looks like this
c/exec sql
c+ UPDATE qgpl.fIncomming
c+ SET F00001 = INSERT(F00001,225,0,';')
c+ WHERE :MaxCount - length(trim(F00001) - length(REPLACE(trim(F00001),';',''))) > 0;
c/end-exec
This is fixed format, notice the c spec. If you are coding in free format on this old release, and need the SQL precompiler to understand what you are doing, you will need to add /end-free and /free compiler directives around the fixed form portions of your code. #Dam included a link in the comments.
I am not authorized [yet] to "comment", so I am using the "answer" feature instead, to solicit more information :-(
Regarding the statement in the OP that "I created 2 tables (PF) on iSeries", the SQL DDL for those two TABLE objects was not included. Having that information would better allow reviewers of the scenario to propose some ideas, using the already-defined TABLEs, rather than the reviewer proposing something else that might possibly be unacceptable within the constraints mandated\established for the scenario; we can only presume that those two tables are a requirement, given the SELECT and UPDATE statements apparently reference those [yet to be described] tables.?
FWiW, when presenting an issue\scenario, offering all of the relevant details [as much as reasonably possible anyhow] is best, to elicit worthwhile feedback; otherwise a topic ends up receiving many comments asking for additional clarification of the scenario. That appears to have transpired already with this topic.

Error DBIF_RSQL_INVALID_RSQL CX_SY_OPEN_SQL_DB in program

I get the following error: DBIF_RSQL_INVALID_RSQL CX_SY_OPEN_SQL_DB (Open SQL command is too big. Condition WHERE of the Open SQL command contains too many conditions). The error points to the following line:
select * from Z3T_MAILS into table t_full_mail UP TO 250 ROWS where ID in r_mid and (p_dat_clause).
Other parts of code:
DATA: p_dat_clause type STRING,
t_full_mail type Z3TT_MAILS,
r_mid type range of Z3E_MAIL_ID.
<...>
if not NOT_READED is initial.
p_clause = 'ISREAD = '''''.
endif.
if DATETO is initial.
p_dateto = DATEFROM.
else.
p_dateto = DATETO.
endif.
if not DATEFROM is initial or not DATETO is initial.
concatenate 'SEND_DATE >= ''' DATEFROM ''' and SEND_DATE <= ''' p_dateto '''' into p_dat_clause.
endif.
<...>
if MAILS is supplied or BODY is supplied or p_dat_clause ne ''.
if not r_mid[] is initial.
select * from Z3T_MAILS into table t_full_mail UP TO 250 ROWS where ID in r_mid and (p_dat_clause).
endif.
endif.
I am new to ABAP and would appreciate any help!
That error happens when you use a range/select-option that has too many entries for the database to handle. The solution to this is always dependent on the use, but in any case you have to limit the number of entries in the range.
In your case you only want up to 250 rows from the database anyway. So, if R_MID is filled with all rows containing a single ID you can check the number of lines in it (LINES( R_MID ) ) and limit it to 250 if there are more than that. In most systems this would get rid of the error.
I'm just guessing here but your range r_mid probably has hundreds of lines that look like this:
r_mid-sign = 'I'.
r_mid-option = 'EQ'.
r_mid-low = '123123123'.
r_mid-high = ''.
So instead you could store those IDs in an internal table. You even might be able to use the internal table you're looping over to fill r_mid in the first place.
Your date variables on the other hand are actually well suited to be declared as a single range:
r_date-sign = 'I'.
r_date-option = 'BT'.
r_date-low = datefrom.
r_date-high = dateto.
Also note the documentation about ranges.
Finally you can write your query as follows:
SELECT *
FROM z3t_mails
INTO TABLE t_full_mail
FOR ALL ENTRIES IN lt_mid
WHERE id EQ lt_mid-id
AND send_date IN r_date.

How to read variable names in a SAS data set?

Are there any statements\functions capable of get the name of variables?
Preferrably putting them into a column of another data set, a text field or a macro variable.
E.g.
- Data set 1
Name age sex
Jk 14 F
FH 34 M
Expected data set
Var_name_of_dataset1
Name
age
sex
PS: I know a statement: select into, which does sth relevantly
It can read the value of a column into a field with customized separetors, and therefore wish there are similar ways of reading column names into a field or a column.
Thanks
PROC CONTENTS would be the quickest way to get that information in a dataset. Column names can be found in the column NAME.
proc contents data=sashelp.class out=contents noprint;
run;
You can also use a datastep and array functions, e.g.
data colnames ;
set sashelp.class (obs=1) ;
array n{*} _NUMERIC_ ;
array c{*} _CHARACTER_ ;
do i = 1 to dim(n) ;
vname = vname(n{i}) ;
output ;
end ;
do i = 1 to dim(c) ;
vname = vname(c{i}) ;
output ;
end ;
run ;
%macro getvars(dsn);
%global vlist;
proc sql;
select name into :vlist separated by ' '
from dictionary.columns
where memname=upcase("&dsn");
quit;
%mend;
This creates a macro variable called &vlist that will contain the names of all the variables in your dataset, separated by a space. If you want commas between the variable names, all you have to do is change the 'separated by' value from ' ' to ', '. The use of the upcase function in the where statement avoids problems with someone passing the dataset name in the wrong case. The global statement is needed since the macro variable created will not necessarily be available outside the macro without defining it as global
Slightly changed from SAS help and documentation.
%macro names(dsid);
%let dsid=%sysfunc(open(&dsid, i));
%let num=%sysfunc(attrn(&dsid,nvars));
%let varlist=;
%do i=1 %to &num ;
%let varlist=&varlist %sysfunc(varname(&dsid, &i));
%end;
%let rc = %sysfunc(close(&dsid)); /*edit by Moody_Mudskipper: omitting this line will lock the dataset */
%put varlist=&varlist;
%mend names;
%names(sasuser.class) ;
Then we preserve case and the order off data, even if numeric and character is mixed.
I'm not sure Rawfocus assertion that reading dictionary tables queries all libraries is true, had the example used sashelp.vcolumn instead then it would be true, that approach is very slow and does access all the libraries allocated. (You can prove this with the SAS RTRACE system option.)
I am of the opinion that a sql query to dictionary.columns is the fastest of the methods outlined here. Obviously the macrotised code would work without the macro but the point of the macro here is I think as a utility; put the code into your favourite macro library and you never need to think about it again.