Mapping an SQL server library in SAS - database

I'm trying to map the source of a dataset i usually access using SQL parse through as a library.
Below is the SQL parse through code I use to access the table
proc sql noprint;
connect to ODBC (DSN='Location1' );
create table test as
Select *
from Connection to ODBC
(
Select *
from CentralDB.dbo.Table_i_want
)
;
disconnect from ODBC
;
quit;
And below is the libname statement I tried writing
LIBNAME mylib ODBC DATASRC='Location1' SCHEMA=dbo ;
The above statement doesn't map it to the correct location, where do I put the CentralDB part?
Can anyone help me create a libname statement out of this?
Thanks,

libname mydblib odbc
noprompt="uid=testuser;pwd=testpass;dsn=sqlservr;"
stringdates=yes;
proc print data=mydblib.customers;
where state='CA';
run;

If you have a metadata server: It is always better to ask your SAS Admin to register the library and tables you want want in SAS metadata / Folders so you will have a permanent and standard way to access them.
Quick Solution:
Use the libname below and update the schema, user and password
LIBNAME mylib ODBC DATASRC=location_1 SCHEMA=dbo USER=sql user PASSWORD="xxx" ;
The step below will list the tables in the library
proc datasets lib=mylib ;
quit;

I always opt to build a connection string. I don't have an example on hand, but you should be able to build a string that specifies both the database CentralDB and the schema dbo.
It will look something like this:
libname mylib odbc noprompt='Driver={SQLServer};Server=Your_Server_Name; Database=CentralDB;Schema=dbo;Uid=Your_Username; Pwd=Your_Password;';

Related

SQL to SAS ODBC connection - truncation of NVARCHAR(max) but getting ERROR: The option dbtype is not a valid output data set option

I have a table stored in SQL Server Management Studio version 15.0.18369.0 and I have a working and established ODBC connection to SAS language program World Programming Software version 3.4.
Previous import/reading of this data has been successful but the operator of the SQL server may have recently converted their data type to NVARCHAR(max). It may also be due to a driver change (I got a new laptop and reinstalled what I thought was the exact same OBDC for SQl driver as I had before but who knows) I have 64-bit ODBC Driver 17 for SQL server.
The VARCHAR(max) type in SQL causes the data to only be 1 character long in every column.
I have tried to fix it by:
Adding the DB max text option to libname
libname odbclib odbc dsn="xxx" user="xxx" password="xxx" DBMAX_TEXT=8000;
This did nothing so I also tried to add DB type option:
data mydata (dbtype=(mycol='char(25)')) ;
set odbclib.'sql data'n
run;
And I get ERROR:
The option dbtype is not a valid output data set option.
I have also tried DBSASTYPE, and putting both options in the set statement and this yields the same error.
I also tried with proc SQL:
proc sql noprint;
CONNECT TO ODBC(dsn="xxx" user="xxx" password="xxx"); create table
extract(compress=no dbsastype=(mycol='CHAR(20)')) as select * from
connection to odbc ( select * from dbo.'sql data'n
); disconnect from odbc; quit;
And I get
NOTE: Connected to DB: BB64 (Microsoft SQL Server version 12.00.2148)
NOTE: Successfully connected to database ODBC as alias ODBC. 3915
create table extract(compress=no
dbsastype=(mycol='CHAR(20)')) as 3916 select *
from connection to odbc 3917 ( 3918 select *
from dbo.'sql data'n ERROR: The option dbsastype is not a valid
output data set option 3919 3920 ); 3921
disconnect from odbc; NOTE: ERRORSTOP was specified. The statement
will not be executed due to an earlier error NOTE: Statements not
executed because of errors detected 3922 quit;
One thing to try would be putting the DBTYPE or DBSASTYPE in the right place.
data mydata;
set odbclib.'sql data'n(dbtype=(mycol='char(25)'));
run;
It is an option that would go on the "set" not the "data" line.
I would also encourage you to contact the developers, as it seems like this is a bug in their implementation; or perhaps try a different ODBC SQL driver.
Maybe a workaround in the meanwhile is to use CONVERT in the passthrough?
proc sql noprint;
CONNECT TO ODBC(dsn="xxx" user="xxx" password="xxx"); create table
extract as select * from
connection to odbc ( select a,b,c,convert(NCHAR(20),mycol) from dbo.'sql data'n
); disconnect from odbc; quit;

How to use schema in DMBS SAS connection

Below script show me table dbo.calendar in SAS:
LIBNAME SQL ODBC DSN='sql server' ;
PROC PRINT DATA=SQL.calendar;
RUN;
When I changed DATA to SQL.dbo.calendar (this is a correct name from SQL Server), I got:
6032 PROC PRINT DATA=SQL.dbo.calendar;
ERROR: Invalid data set name SQL.dbo.calendar.
Why and how to choose other schemas in SAS?
The default schema is defined in the DSN. Use the SCHEMA= option in the LIBNAME Statement
LIBNAME SQL ODBC DSN='sql server' schema=<something other than default> ;
http://support.sas.com/documentation/cdl/en/acreldb/69039/HTML/default/viewer.htm#p0bu3zsz1a08ton1msxdx1jo45np.htm

WHERE clause operator requires compatible variables

Below script (fx is SQL Server's table):
LIBNAME SQL ODBC DSN='sql server' ;
DATA new;
SET SQL.fx;
WHERE repo_date = '2016-04-29 00:00:00.000';
RUN;
PROC PRINT DATA=new;
RUN;
returns me an error (in SAS log):
191 WHERE repo_date = '2016-04-29 00:00:00.000';
ERROR: WHERE clause operator requires compatible variables.
192 RUN;
Where can I check which data conversion I need (in this case and in others)?
In SQL Server 2008R2 repo_date is a datetime column.
You are comparing a string to a numeric value.
So your datetime-format is wrong (like Heinzi mentioned), and also you have to convert it to a datetime value (by adding a dt at the end)
Working should this:
WHERE repo_date ='29APR2016 00:00:00.000'dt;
If repo_time is datetime and the time is not relevant, you can just compare the date:
WHERE datepart(repo_date) = '29APR2016'd;
SAS is using the ODBC libname engine to translate SAS data step code into SQL code. Because you're writing it in SAS, SAS is assuming that you are looking for the string 2016-04-29 00:00:00.000. Instead, you want to put it in a SAS date literal so SAS knows how to translate the data.
LIBNAME SQL ODBC DSN='sql server' ;
DATA new;
SET SQL.fx;
WHERE repo_date = '29APR2016:00:00:00'dt;
RUN;
PROC PRINT DATA=new;
RUN;
If you were doing SQL passthrough to directly run SQL on the server, then your above code would work.
proc sql noprint;
connect to odbc(datasrc='sql server');
create table new as
select * from connection to odbc
(select * from schema.fx where repo_date='2016-04-29 00:00:00.000');
disconnect from odbc;
quit;
Basically, what the above is doing is having the SQL server pull the columns, then SAS simply pulls it all over to itself. Think of it as using SAS as a proxy program to run commands directly on the SQL server.

Run SQL statement from file to create data in SAS

I have very little experience in SAS. I do have experience in SQL.
I want to do the following:
- Use a SQL statement that is stored in a text file to import data into SAS.
What works is to copy and paste the SQL server query and run it as a pass-through query in SAS. I get the data (after a few minutes).
But I would like to be able to manage and develop the SQL script in SSMS, and store the script in a sql file. So I tried the following:
proc sql;
connect to ODBC("dsn=DatabaseOfInterest");
create table NewDataSet as select * from connection to odbc(
%include 'C:\sqlscript.sql';
);
quit ;
This does not work and creates the following error:
**ERROR: CLI prepare error:
[Microsoft][ODBC SQL Server Driver][SQL Server]Incorrect syntax near '%'.
**
Is there a way to achieve this?
I don't know if there's a truly clean way to work around this. The issue is that the connect to SQL is passing %include to the SQL parser, which is of course incorrect compared to what you intend.
It will, however, correctly resolve macros and macro variables, so you can read your SQL command into a macro variable and use it that way. One way to do that is below.
filename tempfile temp; *imaginary file - this would be your SQL script;
data _null_; *creating a mocked up SQL script file;
file tempfile;
put "select * from country";
run;
data _null_; *reading the SQL script into a variable, hopefully under 32767?;
infile tempfile recfm=f lrecl=32767 pad;
input #1 sqlcode $32767.;
call symputx('sqlcode',sqlcode); *putting it into a macro variable;
run;
proc sql; *using it;
connect to oledb(init_string=&dev_string);
select * from connection to oledb(
&sqlcode.
);
quit;
The file containing your SQL code is C:\sqlscript.sql. I'll assume it looks something like this:
select * from mytable;
Edit the file so that it now looks like this...
%macro sqlscript;
select * from mytable;
%mend;
... and then rename the file extension to C:\sqlscript.sas.
Finally, change your proc sql code to look like this:
options sasautos = ("c:\", sasautos);
proc sql;
connect to ODBC("dsn=DatabaseOfInterest");
create table NewDataSet as select * from connection to odbc
(
%sqlscript;
);
quit;
Explanation: The %include statement you tried to use although it uses a % sign and looks like macro code can't really be substituted in any random point in code as it is a SAS statement. It's really meant to be issued outside of PROC statements and data steps (it probably shouldn't even have a %sign infront of it but unforunately that's how SAS designed it...). So that's why it won't work.
SAS provides the ability to search for macro functions outside of the current program being run. If you call a macro function that isn't defined in your current SAS program (in this case %sqlscript), it's going to go look for it in the list of pathnames specified in the SASAUTOS option. If it finds a file in one of the SASAUTOS pathnames that exactly matches the macro it's searching for, and if the contents of that file contain a definition for the macro, SAS will compile and run that macro. In the above example, the macro simply substitutes in the SQL code contained within it.
In the options sasautos= statement - we are simply prepending the c:\ path to the existing list of pathnames currently in SASAUTOS. It will search the pathnames in order, and I'm assuming we want our custom macros to override any existing macros if there happens to be a conflict. You only need to specifyt options sasautos= once per SAS session, so don't copy/paste it before every proc sql statement.
Documentation for SASAUTOS . These are also known as autocall macros so that should turn up some useful hits in google too.
Also - obviously I don't recommend storing code in c:\ so adjust as necessary. A note to non-windows users - macro names and definitions are case sensitive so be consistent!
Based on feedback from my previous answer I've provided an alternate approach below that should better address your exact needs.
The code below shows how the final program will 'work' once it is all combined together. We are going to take this code and split it into different files as indicated by the comments:
%macro myQuery; /* FILE 1 - header.sas */
select * from myTable; /* FILE 2 - query.sql */
%mend; /* FILE 3 - footer.sas */
/* BEGIN FILE 4 - main.sas */
proc sql;
connect to ODBC("dsn=DatabaseOfInterest");
create table NewDataSet as
select *
from connection to odbc
(
%myQuery;
);
quit ;
/* END FILE 4 */
FILE1 - "header.sas" will look like:
%macro myQuery;
FILE2 - "query.sql" will look like:
select * from myTable;
FILE3 - "footer.sas" will look like:
%mend;
FILE4 will become:
%include "c:\header.sas"
"c:\query.sql"
"c:\footer.sas"
;
proc sql;
connect to ODBC("dsn=DatabaseOfInterest");
create table NewDataSet as
select *
from connection to odbc
(
%myQuery;
);
quit ;
You can see that we are defining the macro using the include statements. The query which is the body of the macro will be kept separate in it's own .sql file. This should allow you to continue to edit/submit your queries via both SAS and your favorite SQL editor. The header and footer files can be re-used if you have multiple query files.

Fully Qualified Names from SQL Server in SAS

I need to be able to specify the schema that I want to access in SAS. I have used a connection string with the following schema=?? but SAS will not let me select or print the contents of any object in the named schema. Has anyone been able to write a PROC SQL statement selecting objects in a schema other than dbo?
Thank you,
SAS does not use fully quallified names from the SQL server but you can direct SAS to a specific schema. The following is an example that uses a libname as connection to a 2008 SQL Server.
proc print data=myDBconn.v_Lots (SCHEMA=SAS);
WHERE Study_ID IS NOT NULL;
run;
proc print data=myDBconn.Drugs (SCHEMA=Pharmacy);
where _drug_id=1;
run;
proc sql;
create table myTest.drugs as ;
(SELECT * FROM myDbconn.drugs (SCHEMA=Pharmacy));
quit;

Resources