Hallo zusammen,
ich möchte erreichen, dass charvars in double quotes eingebettet sind, das funktioniert auch soweit. Do-Loop im letzten Datastep über alle charvars
mit byte(34) davor und dahinter. Der SAS-Dataset sieht auch wie gewünscht aus (1.Screenshot). Der anschließende PROC EXPORT fügt jeweils vorne wie hinten noch einmal zwei double quotes hinzu (2. Screenshot). Hat jemand eine Ahnung warum und falls möglich, was zu ändern ist?
Das Coding ist ganz unten, das ist ein Auschnitt aus einem Call Execute, es sollte aber lesbar sein.
VG
Wolfgang
data _null_;
set sas_meta.META_TO_SDP(where=(memname='META_FKG_CLUSTERBUENDEL'));
call execute(
'filename out "&path.liefer_oref/tables/'!!compress(memname)!!'.csv";
options mprint;
data work.'!!compress(memname)!!compbl(drop_date)!!';
/*
Vor dem SET-Statement die Reihenfolge festlegen und das Length-Statement erhöhen,
damit durch Anhängen der DoubleQuotes die Länge nicht überschritten und somit abgeschnitten wird.
*/
length '!!chars!!' $1024;
format '!!chars!!' $1024.;
retain '!!retains!!' "";
set sas_meta.'!!compress(memname)!!compress(where_date)!!';
/* Array aller Character, Do-Loop über alle Chars und in DoubleQuotes byte(34) einschließen*/
array Chars[*] _character_;
do i = 1 to dim(Chars);
Chars[i] = cats(byte(34),compress(Chars[i]),byte(34));
end;
drop i;
run;
proc export data=work.'!!compress(memname)!!' file=out dbms=csv replace;
delimiter=";";
run;
filename out clear;
'
);
run;
If the goal is to give SNOWFLAKE extra hints that a field should be handled as character then perhaps you can just use ODS CSV to create the file instead of PROC EXPORT? That tool will add quotes around every character value, whether or not the value actually NEEDS to have quotes around it.
ods csv file=csv;
proc print data=sashelp.class noobs; run;
ods csv close;
Result:
"Name","Sex","Age","Height","Weight" "Alfred","M",14,69.0,112.5 "Alice","F",13,56.5,84.0 "Barbara","F",13,65.3,98.0 "Carol","F",14,62.8,102.5 "Henry","M",14,63.5,102.5 "James","M",12,57.3,83.0 "Jane","F",12,59.8,84.5 "Janet","F",15,62.5,112.5 "Jeffrey","M",13,62.5,84.0 "John","M",12,59.0,99.5 "Joyce","F",11,51.3,50.5 "Judy","F",14,64.3,90.0 "Louise","F",12,56.3,77.0 "Mary","F",15,66.5,112.0 "Philip","M",16,72.0,150.0 "Robert","M",12,64.8,128.0 "Ronald","M",15,67.0,133.0 "Thomas","M",11,57.5,85.0 "William","M",15,66.5,112.0
Or you would write your own data step (or generate it from the metadata of the dataset) and use the ~ modifier in the PUT statement after each character variable. Then you could also switch to using semicolon instead of comma as the delimiter.
data _null_;
file csv dsd dlm=';' ;
set sashelp.class;
put name ~ sex ~ age height weight;
run;
Result:
"Alfred";"M";14;69;112.5 "Alice";"F";13;56.5;84 "Barbara";"F";13;65.3;98 "Carol";"F";14;62.8;102.5 "Henry";"M";14;63.5;102.5 "James";"M";12;57.3;83 "Jane";"F";12;59.8;84.5 "Janet";"F";15;62.5;112.5 "Jeffrey";"M";13;62.5;84 "John";"M";12;59;99.5 "Joyce";"F";11;51.3;50.5 "Judy";"F";14;64.3;90 "Louise";"F";12;56.3;77 "Mary";"F";15;66.5;112 "Philip";"M";16;72;150 "Robert";"M";12;64.8;128 "Ronald";"M";15;67;133 "Thomas";"M";11;57.5;85 "William";"M";15;66.5;112
There is nothing that needs to change. That is how delimited files work.
If your data has double quote characters in it then those need to be quoted when writing a delimited file. Otherwise there is confusion between the double quotes that are in the data and those that are required to protected embedded delimiters in the data.
Gefällt die Lösung?
Die Datei über einen Data step mit file-statement rauslassen.
Das Setzen der Gänsefüßchen erfolgt hier anders als bei meiner Lösung 1. Da war mir nicht klar, dass ein Export nach csv gebraucht wird.
%let M_datei= sashelp.class;
proc contents
data =&M_datei.
out= char_vari (keep= name type)
noprint;
run;
proc sql noprint;
select name
into :M_variliste
separated by ' '
from char_vari
where type = 2;
quit;
data d (keep = &M_variliste. hiwi);
format lfd_nr $4.;
set sashelp.class;
hiwi= catx('"; "' , of &M_variliste.);
hiwi= catt('"',hiwi,'"');
run ;
data d_ex;
set d;
file "&M_output.d.CSV"
RECFM=V
LRECL=800
encoding='wlatin1'
delimiter=";";
if _N_ = 1 then do;
put "&M_variliste.";
end;
put&M_variliste.;
run;
lg eric
Sorry, waren noch 2 Fehler drin.
Nun die richtig Lösung
%let M_datei= sashelp.class;
proc contents
data =&M_datei.
out= char_vari (keep= name type)
noprint;
run;
proc sql noprint;
select name
into :M_variliste
separated by ' '
from char_vari
where type = 2;
select name
into :M_variliste2
separated by ';'
from char_vari
where type = 2;
quit;
data d (keep = &M_variliste. hiwi);
format lfd_nr $4.;
set sashelp.class;
hiwi= catx('"; "' , of &M_variliste.);
hiwi= catt('"',hiwi,'"');
run ;
data d_ex;
set d;
file "&M_output.d.CSV"
RECFM=V
LRECL=800
encoding='wlatin1'
delimiter=";";
if _N_ = 1 then do;
put "&M_variliste2.";
end;
put hiwi;
run;
Das Problem ist, dass wir in ein sog. S3-Bucket nach Snowflake reinschreiben, die Struktur muss erhalten bleiben und mit der DDL der Tabelle übereinstimmen, zudem muss ich zwischen chars und nums unterscheiden. Ich denke, da muss ich einfach basteln, nums und chars separat behandeln, die DDLs nochmal anpassen und die csv-Datei zu Fuß schreiben. Ich dachte da gibt es einen schöneren Weg. Trotzdem danke für die Lösungsansätze, ich denke daraus kann ich etwas machen.
LG, Wolfgang
If the goal is to give SNOWFLAKE extra hints that a field should be handled as character then perhaps you can just use ODS CSV to create the file instead of PROC EXPORT? That tool will add quotes around every character value, whether or not the value actually NEEDS to have quotes around it.
ods csv file=csv;
proc print data=sashelp.class noobs; run;
ods csv close;
Result:
"Name","Sex","Age","Height","Weight" "Alfred","M",14,69.0,112.5 "Alice","F",13,56.5,84.0 "Barbara","F",13,65.3,98.0 "Carol","F",14,62.8,102.5 "Henry","M",14,63.5,102.5 "James","M",12,57.3,83.0 "Jane","F",12,59.8,84.5 "Janet","F",15,62.5,112.5 "Jeffrey","M",13,62.5,84.0 "John","M",12,59.0,99.5 "Joyce","F",11,51.3,50.5 "Judy","F",14,64.3,90.0 "Louise","F",12,56.3,77.0 "Mary","F",15,66.5,112.0 "Philip","M",16,72.0,150.0 "Robert","M",12,64.8,128.0 "Ronald","M",15,67.0,133.0 "Thomas","M",11,57.5,85.0 "William","M",15,66.5,112.0
Or you would write your own data step (or generate it from the metadata of the dataset) and use the ~ modifier in the PUT statement after each character variable. Then you could also switch to using semicolon instead of comma as the delimiter.
data _null_;
file csv dsd dlm=';' ;
set sashelp.class;
put name ~ sex ~ age height weight;
run;
Result:
"Alfred";"M";14;69;112.5 "Alice";"F";13;56.5;84 "Barbara";"F";13;65.3;98 "Carol";"F";14;62.8;102.5 "Henry";"M";14;63.5;102.5 "James";"M";12;57.3;83 "Jane";"F";12;59.8;84.5 "Janet";"F";15;62.5;112.5 "Jeffrey";"M";13;62.5;84 "John";"M";12;59;99.5 "Joyce";"F";11;51.3;50.5 "Judy";"F";14;64.3;90 "Louise";"F";12;56.3;77 "Mary";"F";15;66.5;112 "Philip";"M";16;72;150 "Robert";"M";12;64.8;128 "Ronald";"M";15;67;133 "Thomas";"M";11;57.5;85 "William";"M";15;66.5;112
Tolle Lösungen von Tom! Danke
Frage:
Gibt es bei der ods-csv Lösung die Möglichkeit die Spaltenüberschriften in der ersten Zeile NICHT in Hochkommata erscheinen zulassen ?
lg eric
Hi Eric,
Lösungsmöglichkeit zu Deiner Frage in meiner Antwort an Tom.
LG, Wolfgang
Danke Tom, das hat sehr geholfen. Ich habe noch modifiziert, weil ich die Header ohne double quotes gebraucht habe.
Ich habe die Header mit PROC EXPORT und obs=0 in eine separate csv-Datei geschrieben und die Daten mit dem ~ modifier geschrieben.
Für das PUT-Statement habe ich die Variablen zusammengebastelt. Die beiden csv-Dateien habe ich dann mit %sysexec und dem Linux cat-Befehl aneinandergehängt
proc sql;
create table metainfo as
select memname, name, type, varnum from sashelp.vcolumn
where upcase(libname)="SASHELP" and upcase(memname)="CLASS"
order by varnum
;
quit;
data outvars(keep=memname outvars);
set metainfo end=lastrec;
length outvars out $1024;
retain outvars;
by varnum;
if _n_=1 then do;
outvars="";
end;
if upcase(type)="CHAR" then out=compbl(cat(upcase(name)," ~ "));
else out=upcase(name);
outvars=catx(" ",outvars,out);
if lastrec;
call symput('outvars',outvars);
run;
%let path=<path>;
filename outhead "&path./header.csv" lrecl=1024;
filename out "&path./data.csv" lrecl=1024;
proc export data=sashelp.class(obs=0) file=outhead dbms=csv replace;
delimiter=",";
run;
data _null_;
file out dsd dlm="," ;
set sashelp.class;
put &outvars.;
run;
filename out clear;
filename outhead clear;
%sysexec cat &path./header.csv &path./data.csv > &path./class.csv;
@Tom @E_Berger Hier noch als Macro
%macro custom_ds_to_csv(path=,libref=,dsname=);
proc sql;
create table metainfo as
select memname, name, type, varnum from sashelp.vcolumn
where upcase(libname)="%upcase(&libref.)" and upcase(memname)="%upcase(&dsname.)"
order by varnum
;
quit;
data outvars(keep=memname outvars);
set metainfo end=lastrec;
length outvars out $1024;
retain outvars;
by varnum;
if _n_=1 then do;
outvars="";
end;
if upcase(type)="CHAR" then out=compbl(cat(upcase(name)," ~ "));
else out=upcase(name);
outvars=catx(" ",outvars,out);
if lastrec;
call symput('outvars',outvars);
run;
filename outhead "&path./&dsname._header.csv" lrecl=1024;
filename out "&path./&dsname._data.csv" lrecl=1024;
proc export data=&libref..&dsname.(obs=0) file=outhead dbms=csv replace;
delimiter=",";
run;
data _null_;
file out dsd dlm="," ;
set &libref..&dsname.;
put &outvars.;
run;
filename out clear;
filename outhead clear;
%sysexec cat &path./&dsname._header.csv &path./&dsname._data.csv > &path./&dsname..csv;
%mend custom_ds_to_csv;
Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9. Sign up by March 14 for just $795.