Writing SAS dates to SQL Server databse - sql

How to write SAS dates to Microsoft SQL Server 2016 Date data type in database?
I got SAS data with a sas date DataEndDay and I want to write that into a database. The following bit is in use (buffer is just to speed up the testing-failing) :
libname valu oledb provider=sqloledb schema="dbo" INSERTBUFF=100
properties=("User ID"="&username." Password="&pw."
"data source" = &database.
"initial catalog"=&catalog.);
proc sql noprint;
insert into valu.Data_upload_from_me
( <some_columns...>,
<more-columns...>
,DataEndDay
)
select
<some_columns_source...>,
<more-columns_source...>
,DataEndDay
from work.SAS_data_to_publish
;quit;
Of course because SAS dates are numbers, direct writing is going to fail. What works is if I hard-code this as:
select
<some_columns_source...>,
<more-columns_source...>
,'2018-12-12'
from work.SAS_data_to_publish
;quit;
But If I convert the SAS date to string in SAS datasteps:
data SAS_data_to_publish ;
set SAS_data_to_publish ;
dataEndday0 = put(DataEndDay, yymmddd10.);
DataEndDay1 = quote(dataEndday0, "'") ;
run;
and try to write either of these, I get conversion error:
ERROR: ICommand::Execute failed. : Conversion failed when converting date and/or time from character string.
When I select the string it looks pretty ok:
proc sql; select DataEndDay1 from SAS_data_to_publish; quit;
'2018-12-12'
previously I've managed to write dateTimes with similar trick, which works:
proc format;
picture sjm
. = .
other='%Y-%0m-%0d %0H:%0M:%0S:000' (datatype=datetime)
;run;
data to_be_written;
set save.raw_data_to_be_written;
DataEndDay0 = put(dhms(DataEndDay,0,0,0), sjm. -L);
run;
Anyone ran into similar issues? How could I write the dates?
I could ask them to change the column to dateTime, maybe....
Thank you in advance.
Edit:
I managed to develop a work-around, which works but is ugly and -frankly- I don't like it. It so happens that my date is same for all rows, so I can assing it to macro variable and then use it in database writing.
data _NULL_;
set SAS_data_to_publish;
call symput('foobar', quote( put (DataEndDay , yymmddd10. -L), "'") ) ;
run;
....
select
<some_columns_source...>,
<more-columns_source...>
,&foobar.
from work.SAS_data_to_publish
;quit;
Of course this would fail immediately should DataEndDay vary, but maybe demonstrates that something is off in Proc SQLs select clause....
Edit Edit Pasted the question to SAS forums

I finally managed to crack the issue. The issue was for the missing values. As I am passing the values as strings into the database the parser interpreted missing values as real dots instead of empty strings. The following works:
data upload;
set upload;
CreatedReportdate2 = PUT(CreatedReportdate , yymmddn8.);
run;
libname uplad_db odbc noprompt =
"DRIVER=SQL Server; server=&server.; Uid=&user.;Pwd=&pw.; DATABASE=&db.;"
INSERTBUFF=32767;
proc sql;
insert into uplad_db.upload_table
(.... )
select
case when CreatedReportdate2 ='.' then '' else CreatedReportdate2 end,
...
from upload;
quit;

SAS does not really properly support the SQL server DATE data type. I imagine this is due to the fact that it's newer, but for whatever reason you have to pass the data as strings.
For missing values, it's important to have a blank string, not a . character. The easiest workaround here is to set:
options missing=' ';
That will allow you to insert data properly. You can then return it to . if you wish. In a production application that might be used by others, I'd consider storing aside the option value temporarily then resetting to that, in order to do no harm.

Normally I just use PROC APPEND to insert observations into a remote database.
proc append base=valu.Data_upload_from_me force
data=work.SAS_data_to_publish
;
run;
Make sure your date variable in your SAS dataset use the same data type as the corresponding variable names in your target database table. So if your MS SQL database uses TIMESTAMP fields for date values then make sure your SAS dataset uses DATETIME values.
If you want to use constants then make sure to use SAS syntax in your SAS code and MS SQL syntax in any pass through code.
data test;
date = '01JAN2017'd ;
datetime = '01JAN2017:00:00'dt ;
run;
proc sql ;
connect to oledb .... ;
execute ( ... date = '2017-01-01' .... datetime='2017-01-01 00:00' ...)
by oledb;
quit;

Related

Exporting csv file with datetime() in the name

I am trying to export a csv file from SAS and adding datetime() to the name of the file. for some reason nothing happens. The export works fine but I don't get the time stamp in the file name.
This is how i create the datetime variable:
data Dato_eksport;
dato_eksport=today();
dato_eksport_f=datetime();
format dato_eksport_f datetime19.;
run;
PROC SQL NOPRINT;
SELECT DISTINCT
dato_eksport_f
INTO :dato_eksport_f
FROM Dato_eksport_F;
I the use this variable in my export:
%LET filtype_csv = .csv;
%LET filnavn_csv = CAT(OUTPUT_DAGLIG_LCR,&dato_eksport_f);
%LET path_csv = \\path\path\path\path\path\path\path;
%LET kombineret_csv = "&path_csv&filnavn_csv&filtype_csv" dlm = ';';
%PUT &kombineret_csv;
%ds2csv (
data=OUTPUT_DAGLIG_LCR,
runmode=b,
csvfile=&kombineret_csv
);
What am I doing wrong the file gets updated but I get no errors and the file is missing the datetime string.
I hope you cna point me in the right direction.
First mistake is the PROC SQL step that is trying to create the macro variable dato_eksport_f is reading from a dataset you have not defined for us.
Second mistake is inserting text like CAT(...) into the macro variable filnavn_csv. To the macro processor everything is text, it only looks to operate on the text when it sees the macro triggers & or %.
To avoid adding leading/trailing spaces into the macro variable when using PROC SQL and the INTO clause make sure to add the TRIMMED keyword.
proc sql noprint;
select dato_eksport_f into :dato_eksport_f trimmed
from Dato_eksport
;
quit;
There is no need to try to use functions to concatenate text in macro code. To append macro variables together just use the syntax you used in this statement:
%LET kombineret_csv = "&path_csv.\&filnavn_csv.&filtype_csv" dlm=';';
If you do want to use a SAS function in macro code you need to call it with the macro function %SYSFUNC(). You could use that to skip the data and proc sql steps and just call the DATETIME() function directly in macro code. Note that the leading space generated by the datetime19. format will be remove by the %LET statement.
%let dato_eksport_f = %sysfunc(datetime(),datetime19.);
Now you can build your full filename:
%LET path_csv = \\path1\path2\path3;
%LET filnavn_csv = OUTPUT_DAGLIG_LCR_&dato_eksport_f;
%LET filtype_csv = .csv;
%LET kombineret_csv = "&path_csv.\&filnavn_csv.&filtype_csv" dlm=';';
%PUT &kombineret_csv;
You might want to use a different string to timestamp your filenames. Something that avoids colons and also will sort properly.
data _null_;
call symputx('dato_eksport_f'
,translate(put(datetime(),e8601dt.),'___','-T:'));
run;
Example:
1806 %put &=dato_eksport_f;
DATO_EKSPORT_F=2022_08_01_09_38_58

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.

Sql equivalent in SAS

I have a code such as below in sql(lot more and and not ins but just wanted to list few) i am new to sas and know proc sql a bit etc, learning and exploring everyday,
Select * from table
Where date=‘20180112’
and type=‘apple’ and location=‘dc’ and not
(columnName)in(‘a’,’b’) And lat=‘ten’
I am not able to understand sas equivalent of above sql as below. Can someone please explain sas code of if part and then do
Data sample;
Set sourcetble;
If date=‘20180112’ and type=‘apple’
And location=‘dc’ then do;
Blah1=‘rain’
Blah2=‘something else’
If columnName in(‘a’, ‘b’) and lat=‘ten’ Then do;
This just subsets based the values and variables in the WHERE statement.
Data sample;
set table;
WHERE date='20180112' and type='apple' And location='dc'
and columnName in (‘a’, ‘b’) and lat=‘ten’;
<other optional code>;
run;
Not like SQL query, a SAS data step will result in creating a new dataset. If you don't need to have a new dataset, you can use "data _null_;". Alternatively there are SAS procedures that will simply display dataset such as SQL "select" would do.
The "set" in SAS is equivalent to the "from" in SQL: it specifies the base dataset(s) from which you build the new dataset.
By default, SAS data step keeps all variables of the "set" datasets. It is equivalent to "select *" in SQL. If you need only some variables, you can use "keep" and "drop" statements in SAS.
The "where" clause and "and"/"or" operators work similarly in SAS and SQL, but with slightly different syntax.
The if … then in the data step has no correspondce to the SQL shown in the question. A conditional assignment in SQL is done using a case statement.
So a DATA step statement such as
data want;
set have;
…
if date="20180112" and type="apple" and location="dc" then do;
Blah1="rain";
Blah2="something else";
end;
would be concordant with SQL
Proc SQL;
create table want as
select …
, case when date="20180112" and type="apple" and location="dc"
then "rain"
else ""
end as Blah1
, case when date="20180112" and type="apple" and location="dc"
then "something else"
else ""
end as Blah2
from
have
…
;
For the case of some algorithm needing to assign several variables at once when some criteria (if logic) is met:
DATA Step has do; … end; syntax which can have several assignments statements within.
SQL select statement can only assign one variable per logic evaluation (case statement), thus the logic code has to be repeated for each variable being assigned based on criteria.

SAS code Proc rank with groups option to SQL conversion

I am new to SAS and struggling to convert the below piece of code to SQL.
proc rank data = input (where = (limit_assort = 1)) out = assort_rank groups=3;
var assort;
ranks assort_rank;
run;
I tried in sql by using the below one but not matching with the SAS output.
Percentile_Cont(0.3) Within Group (Order by assortment) As assortment_rank
In SAS output, i can see a column "Rank for variable assort"with 0,1&2 based on the groups=3 option. But not sure how to handle it in SQl.
Thanks in advance!

Convert date into varchar with sas

I really hope you can assist.
When I run this code:
libname odbc ### user='abc' password='****' dsn='bleh' schema='dbo';
%let date=%sysfunc(intnx(day,%sysfunc(today()),-1,b),yymmddd10.);
%put &date.;
run;
It works!
But if I run it with the call execute I get this error – it reads from sql – yet the date in sql is varchar:
data _null_;
set odbc.SQLTableName;
if ((date= &date.) and (dateComplete ne .))then call execute("%include 'path';");
run;
dateComplete=Jun 10 2015 1:54PM _ERROR_=1 _N_=1
I am looking for a way to convert my date.
So it reads today()-1 (Technically yesterday’s date)
YOUR HELP WILL BE GREATLY APPRECIATED!!!
Shouldn't you be using single quotes instead of double quotes? As you don't want your macro to be executed before the data step ends?
call execute("%include 'path';");
Try:
call execute('%include "path";');