I am testing a very small macro which uses a Proc SQL command to extract from a SQL database. I have used this many times and understand how it works but this time it is stumping me!
%macro Correlation(dsn,db,prevdb,prodcode,sqltable,var,brick);
proc sql;
connect to ODBC (required="DSN=&dsn;" );
create table comp_correlation as select * from connection to ODBC
(select a.Table_Name,
a.Variable,
a.Correlation as Current_Corr
from DBTest.dbo.MetaData as a
where Product_Code=&prodcode. and Table_Name=&sqltable. and
variable=&var.);
disconnect from ODBC;
quit;
%mend;
I am then calling this macro with the following parameters. My server name is censored but I can assure you it connects successfully.
%Correlation(********, A2019, A2018, HouseValues, Houses, Value);
However, it returns the following error:
[Microsoft][SQL Server Native Client 11.0][SQL Server]Invalid column name 'HouseValues'.
Anyone know why this is happening? I am not even trying to select the Product Code as a column, it is only in my where statement?
it seems you have missed one parameter in the below call :
%Correlation(********, A2019, A2018, HouseValues, Houses, Value);
Is it a typo or i interpreted it wrong ?
thanks
I have now fixed this using the %bquote macro.
%macro Correlation(dsn,db,prevdb,prodcode,sqltable,var,brick);
proc sql;
connect to ODBC (required="DSN=&dsn;" );
create table comp_correlation as select * from connection to ODBC
(select a.Table_Name,
a.Variable,
a.Correlation as Current_Corr
from DBTest.dbo.MetaData as a
where Product_Code=%bquote('&prodcode') and Table_Name=%bquote('&sqltable') and
variable=%bquote('&var'));
disconnect from ODBC;
quit;
%mend;
Sounds like your actual error was that you passed a string value to your remote database without quotes. You could solve that by passing the quotes in the macro call.
%macro Correlation(dsn,db,prevdb,prodcode,sqltable,var,brick);
proc sql;
connect to ODBC (required="DSN=&dsn;" );
create table comp_correlation as select * from connection to ODBC
(select a.Table_Name,
a.Variable,
a.Correlation as Current_Corr
from DBTest.dbo.MetaData as a
where Product_Code=&prodcode. and Table_Name=&sqltable. and
variable=&var.);
disconnect from ODBC;
quit;
%mend;
%Correlation(dsn=********,db=A2019,prevdb=A2018
,prodcode='HouseValues', sqltable='Houses', var='Value');
Note that you can use parameter names in the macro call, even for parameters that are defined to allow them to also be called by position.
You could also make your macro a little smarter and have it add the quotes. You could even make it smart enough to remove any optional existing double quotes around the value and replace them with single quotes.
%let prodcode=%sysfunc(quote(%qsysfunc(dequote(%superq(prodcode)))),%str(%'));
Then you could call it anyway you want.
prodcode=HouseValues
prodcode="HouseValues"
prodcode='HouseValues'
Related
I have a store procedure that takes in a uniqueidentifier and returns a table. What I am trying to do is to use these results like a regular table, but I realize SQL is still limited and doesn't allow it (yet!)
Example:
SELECT *
FROM MyTable MT
WHERE NOT EXISTS
(SELECT *
FROM MyStoredProc(MT.ID) MSP
WHERE MT.SomeData = MSP.SomeData)
Is there an alternative solution to this problem? And please don't suggest using a function or a view as they are not exchangeable with a stored procedure. I'm actually quite surprised that the developers haven't implemented this yet considering the age of SQL.
Any help would be really appreciated!
That syntax won't work. You'd have to dump the results of the stored proc into a temp table and the reference the temp table.
Because Of the parameter, I'd suggest either making a new version of the proc that can execute for all IDs at once or drop the proc code into a table valued function (which can be executed using the syntax you're trying to use).
Here is my code:
PROC SQL;
connect to odbc (dsn=ODC uid=sa pwd=XXXXX);
EXECUTE ( INSERT INTO dbo.tblDLA_Backup SELECT * FROM &dlafile.) BY ODBC;
disconnect from odbc;
quit;
Im getting this error
ERROR: CLI execute error: [Microsoft][SQL Server Native Client 11.0][SQL Server]Invalid object name 'work.dlabackup'.
If i do this:
proc sql;
connect to odbc (dsn=ODC uid=sa pwd=XXXXX);
insert into tblDLA_Backup SELECT * FROM WORK.DLABACKUP;
disconnect from odbc;
quit;
I get this error:
ERROR: File WORK.TBLDLA_BACKUP.DATA does not exist.
Why is it that I can't reference my SAS dataset and just insert? it should be simple as that..
The first error occurs because you are executing an SQL instruction on SQL Server, and not locally. And since this instruction contains a reference to your local SAS dataset, an error occurs because SQL server thinks it is a table in its own database.
You take a wrong approach on that one.
Your second approach is correct because you are executing the SQL in SAS, which both knows the local dataset and the SQL server tables. And it is syntactically valid at first glance.
The error is clear: SAS doesn't found the local dataset WORK.TBLDLA_BACKUP
Thus, verify if this dataset exists and is not corrupted:
in your explorer window, click on Libraries, then Work, and verify if TBLDLA_BACKUP is there, and if yes open it and check if you see your data.
I can't say more at this point, but you should hopefully discover something.
You need to use libref it you want to write into the foreign database.
libname sqldb odbc dsn=ODC uid=sa pwd=XXXXX ;
PROC SQL;
INSERT INTO SQLDB.tblDLA_Backup SELECT * FROM &dlafile.;
quit;
Note that you also need fix it so that your macro variable contains the name of a table that exists.
I am not sure why I would have an error when trying to connect to odbc
proc sql;
24 create table xxx.test as
25 (select * from nxxxe.nxrxxt);
1) the connection is successful
2) libref successful
I did not execute any queries. How come I have a syntax error? I even did not have any )
You do have parentheses in your query; try removing them:
proc sql;
create table kyle.test as
select *
from noagree.no_agreement_list;
quit;
But try this with a small table to begin with. Also, especially with Teradata, it's very important that you specify the index. Using the code you have, the first SAS variable in the data set will be used as the index. A poorly chosen index can have seriously negative consequences on your database (affecting other users as well as yourself.
I use SAS/Access to Teradata myself so I don't know if these options are available with ODBC, but here is an example of how to explicitly define the index and various column types:
data kyle.test
( dbcreate_table_opts='primary index(INDEX_COLUMN)'
dbtype=( INDEX_COLUMN='INTEGER NOT NULL'
, USER_NAME='VARCHAR(120) CHARACTER SET LATIN NOT CASESPECIFIC'
, PHONE_NUMBER='CHAR(10)'
, CONTACT_DATE="DATE FORMAT 'yyyy-mm-dd'"
, FROM_LINE_NUM='SMALLINT'
, DOLLAR_DATA='DECIMAL(15,2)'
)
);
set noagree.no_agreement_list;
run;
Note this uses a normal SAS data step rather than PROC SQL; any variable in the data set that is not listed in the dbtype option will be copied using the standard SAS conversion.
Finally, once you get this to work, be sure to run a 'collect statistics' step, which you can do using the SQL Assistant tool (SQLA). I assume you have access to SQLA, especially if you have write permission to some database.
I have a query that connects to remote database and brings back results bit it in a function as shown below.
CREATE OR REPLACE FUNCTION get_users()
$BODY$
BEGIN
RETURN QUERY
SELECT c.user_id, c.user_name, c.user_subscrib
FROM dblink('remote_db1', 'select user_id, user_name, user_subscrib from users_tbl')
AS c(user_id int, user_name varchar, user_subscrib varchar);
END;
$BODY$;
LANGUAGE plpgsql;
Once this function is called like select * from get_users(); it returns the result of the inner query which connects to remote database, executes and conveniently brings the results back. My question is, do I have to close the dblink or it will automatically close?
Thank you in advance for your help.
Assuming that you have previously used dblink_connect to connect and named the connection "remote_db1" then you will need to do a dblink_disconnect on the named connection.
PostgreSQL dblink
If you include the connection string itself in place of using the named connection then the connection will only last as long as the query and then close itself.
I am answering this question.
Yes it does automatically close connection. You do not need to use db_link connect/disconnect explicitly when you name the dblink your connecting within the query as shown above.
Using SQL Server 2005, I'd like to run a stored procedure and insert all of the results into a new table.
I'd like the new table to have its columns automatically configured based upon the data returned by the stored procedure.
I am familiar with using the SELECT ... INTO syntax:
SELECT * INTO newtable FROM oldtable
Is this possible?
Edit for clarification: I'm hoping to accomplish something like:
Select * INTO newtable FROM exec My_SP
The only way to do this is w/ OPENROWSET against the local server:
SELECT * INTO #temp
FROM OPENROWSET (
'SQLOLEDB'
, 'Server=(local);TRUSTED_CONNECTION=YES;'
, 'SET FMTONLY OFF EXEC database.schema.procname'
) a
But this is kind of a last-ditch-gotta-do-it-damn-the-consequences kind of method. It requires elevated permissions, won't work for all procedures, and is generally inefficient.
More info and some alternatives here: http://www.sommarskog.se/share_data.html
This seems like a horrible design. You're really going to create a new table to store the results of a stored procedure, every time the stored procedure is called? And you really can't create the table in advance because you have absolutely no idea what kind of output the stored procedure has? What if the stored procedure returns multiple resultsets? What if it has side effects?
Okay, well, if that's what you really want to do...
One way to accomplish this is to use your local server as a linked server and utilize OPENQUERY. First you need to make sure your local server is configured for data access:
EXEC sp_serveroption 'local server name', 'DATA ACCESS', true;
Then you can do something like this:
SELECT * INTO dbo.newtable
FROM OPENQUERY('local server name', 'EXEC yourdb.dbo.yourproc;');
PS How are you going to write code that is going to perform SELECT INTO into a new table name every time (because you can only do SELECT INTO once)? Dynamic SQL? What happens if two users run this code at the same time? Does one of them win, and the other one just gets an error message?
A variation of the same is
create table somename
select * from wherever;