I'm trying to avoid the error:
ERROR: Teradata execute: Object 'MY_TABLE' does not exist.
When executing TERADATA SQL from SAS
This is the original SAS code I'm using:
proc sql;
connect to TERADATA (user='my_user' password=XXXXXXXXXX MODE=TERADATA TDPID='bdpr');
execute(database MY_DB) by TERADATA;
execute(Drop table MY_TABLE;) by TERADATA;
disconnect from TERADATA;
quit;
According to the documentation the .SET ERRORLEVEL 3807 SEVERITY 0 should fix my problem.
I tried inserting the following before my DROP TABLE statement:
execute(.SET ERRORLEVEL 3807 SEVERITY 0) by TERADATA;
execute(ECHO '.SET ERRORLEVEL 3807 SEVERITY 0') by TERADATA;
I tried combining both:
execute(ECHO '.SET ERRORLEVEL 3807 SEVERITY 0'; Drop table MY_TABLE;) TERADATA;
With either a syntax error for the calls without ECHO or no effect on the error when trying the ECHO variants.
The problem is that the .SET ERRORLEVEL is not a SQL statement but a BTEQ command. According to the docs it should be possible to execute BTEQ commands from standard TERADATA SQL should be possible using the ECHO construct. But from SAS this doesn't seem to be working.
I only need a solution to avoid the SAS error, both SAS side solutions as well as TERADATA solutions are ok for me.
Why not ask Teradata if the table exists and then have SAS conditionally run the drop?
%let tablekind=NONE;
select obj into :tablekind trimmed from connection to teradata
(select case when (tablekind in ('T','O')) then 'TABLE'
else 'VIEW' end as obj
from dbc.tablesv
where databasename = 'MY_DB' and tablename= 'MY_TABLE'
and tablekind in ('V','T','O')
)
;
%if &tablekind ne NONE %then %do;
execute(drop &tablekind. MY_DB.MY_TABLE;) by teradata;
%end;
I don't know the syntax to create an SP from SAS, but I doubt you will have the right to do so.
You might ask your DBA to install following SP, which drops any kind of table (including Volatile).
--Drop a table without failing if the table doesn’t exist:
REPLACE PROCEDURE drop_table_if_exists
(
/* database name, uses default database when NULL.
Must be calling user for Volatile Tables
*/
IN db_name VARCHAR(128) CHARACTER SET Unicode,
/* table name */
IN tbl_name VARCHAR(128) CHARACTER SET Unicode,
OUT msg VARCHAR(400) CHARACTER SET Unicode
) SQL SECURITY INVOKER – check the rights of the calling user
BEGIN
DECLARE full_name VARCHAR(361) CHARACTER SET Unicode;
DECLARE sql_stmt VARCHAR(500) CHARACTER SET Unicode;
DECLARE exit HANDLER FOR SqlException
BEGIN
-- catch "table doesn't exist" error
IF SqlCode = 3807 THEN SET msg = full_name || ' doesn''t exist.';
ELSE
-- fail on any other error, e.g. missing access rights or wrong object tye
RESIGNAL;
END IF;
END;
SET full_name = '"' || Coalesce(db_name,DATABASE) || '"."' || Coalesce(tbl_name,'') || '"';
SET sql_stmt = 'DROP TABLE ' || full_name || ';';
EXECUTE IMMEDIATE sql_stmt;
SET msg = full_name || ' dropped.';
END;
Maybe this could help you, if you run this as a proc call:
replace procedure drop_if_exists( in_object varchar(50))
begin
IF EXISTS(SELECT 1 FROM dbc.tables WHERE tablename = in_object
and databasename='<your database name>') THEN
CALL DBC.SysExecSQL('DROP TABLE ' || in_object);
END IF;
END;
And call this from sas via:
execute (call drop_if_exists(MY_TABLE);) by TERADATA;
EDIT: SAS-invoked procedure creation
proc sql;
connect using DBCONN;
execute(
replace procedure drop_if_exists( in_object varchar(50))
begin
IF EXISTS(SELECT 1 FROM dbc.tables WHERE tablename = in_object
and databasename='<my database>') THEN
CALL DBC.SysExecSQL('DROP TABLE ' || in_object);
END IF;
END;
) by DBCONN;
disconnect from DBCONN;
quit;
Related
I have metrics sas table like below
work.met_table
Metrics_Id Metrics_desc
1 Count_Column
2 Sum_Column
3 Eliminate_column
I wanna do something like doing while loop in T-sql
select count(*) :cnt_tbl from work.met_table
%let init_cnt = 1
while (&init_cnt = &cnt_tbl)
begin
select Metrics_desc into :met_nm
from work.met_table
where metrics_id = 1
Insert into some_sas_table
Select * from another table where Metrics_desc =&met_nm
/* Here I wanna loop all values in metrics table one by one */
end
%put &init_cnt = &int_cnt+1;
How this can be done in proc sql? Thanks in advance
If you want to dynamically generate code then use the SAS macro language.
But for your example there is no need to dynamically generate code.
proc sql ;
insert into some_sas_table
select *
from another_table
where Metrics_desc in (select Metrics_desc from work.met_table)
;
quit;
You can also do an explicit pass through. Send your native t-sql code to run on the database Server through SAS rather than bringing the data to the SAS application server to query it.
The example below is explained in details here.
PROC SQL;
CONNECT TO ODBC(DATASRC=SQLdb USER=&SYSUSERID) ;
/* Explicit PASSTHRU with SELECT */
SELECT *
FROM CONNECTION TO ODBC (
SELECT b.idnum o.[SSdatecol] AS mydate
FROM dbo.big_SS_table1 b
LEFT JOIN dbo.other_SStable o
ON b.idnum = o.memberid
WHERE o.otherdatecol >= '2014-10-06'
--This is a T-SQL comment that works inside SQL Server
) ;
;
DISCONNECT FROM ODBC ;
QUIT;
I want to store count of dataset in variable like below
%let Cnt ;
create table work.delaycheck as
select * from connection to oracle
(
SELECT PTNR_ID,CLNT_ID,REPORTING_DATE_KEY,NET_SALES
FROM FACT_TABLE
MINUS
SELECT PTNR_ID,CLNT_ID,REPORTING_DATE_KEY,NET_SALES
FROM HIST_FCT
);
I want to store count of this table in the variable Cnt like below
%put = (Select count(*) from work.delaycheck )
And Then
If(Cnt=0)
THEN
DO NOTHING
ELSE
execute(
Insert into Oracle_table
select * from work.delaycheck
) by oracle;
disconnect from oracle;
quit;
How can I acheive these steps? Thanks In advance!!
All of the SQL and data shown is occurring remotely. You can perform all the activity there without involving SAS. Oracle will process
PROC SQL;
CONNECT TO ORACLE ...;
EXECUTE (
INSERT INTO <TARGET_TABLE>
SELECT * FROM
( SELECT PTNR_ID,CLNT_ID,REPORTING_DATE_KEY,NET_SALES
FROM FACT_TABLE
MINUS
SELECT PTNR_ID,CLNT_ID,REPORTING_DATE_KEY,NET_SALES
FROM HIST_FCT
)
) BY ORACLE;
and not insert any records if the fact table is comprised of only historical facts.
EXECUTE can also submit PL/SQL statements, which in-turn can reduce the need for extraneous system interplay.
Delete this line from your code
%let Cnt ;
In order to get the count: Add the code below which will create the macro variable Cnt with the count:
proc sql;
Select count(*) into: Cnt from work.delaycheck ;
quit;
Update the if statement: the "&" is used to reference macro variables
If &cnt=0
The Code below shows how to use the if/else and the use of Call Execute:
data _null_;
if &cnt=0 then put 'Cnt is 0';/*if true: a note is written to the log*/
else call execute ('proc print data=work.e; run;');
/*else clause: the Proc Print code is executed*/
run;
I'm still new to SAS and DB2. I have a DB2 Table with a column that stores values encoded as timestamps. I'm trying to load data onto this column from a SAS data set in my Work directory. Some of these timestamps, however, correspond to dates before 01-01-1582 and can not be stored as datetime values in SAS. They are instead stored as strings.
This means that if I want to load these values onto the DB2 table I must first convert them to timestamp with the TIMESTAMP() DB2 function, which, as far as I know, requires passthrough SQL with an execute statement (as opposed to the SAS ACCESS libname method). For instance, in order to write a single value I do the following:
PROC SQL;
connect to db2 (user = xxxx database = xxxx password = xxxx);
execute (insert into xxxx.xxxx (var) values (TIMESTAMP('0001-01-01-00.00.00.000000'))) by db2;
disconnect from db2;
quit;
How can I achieve this for all values in the source data set? A select ... from statement inside the execute command doesn't work because as far as I know I can't reference the SAS Work directory from within the DB2 connection.
Ultimately I could write a macro that executes the PROC SQL block above and call it from within a data step for every observation but I was wondering if there's an easier way to do this. Changing the types of the variables is not an option.
Thanks in advance.
A convoluted way of working around that would be to use call execute:
data _null_;
set sas_table;
call execute("PROC SQL;
connect to db2 (user = xxxx database = xxxx password = xxxx);
execute (
insert into xxxx.xxxx (var)
values (TIMESTAMP('"||strip(dt_string)||"'))
) by db2;
disconnect from db2;
quit;");
run;
Where sas_table is your SAS dataset containing the datetime values stored as strings and in a variable called dt_string.
What happens here is that, for each observation in a dataset, SAS will execute the argument of the execute call routine, each time with the current value of dt_string.
Another method using macros instead of call execute to do essentially the same thing:
%macro insert_timestamp;
%let refid = %sysfunc(open(sas_table));
%let refrc = %sysfunc(fetch(&refid.));
%do %while(not &refrc.);
%let var = %sysfunc(getvarc(&refid.,%sysfunc(varnum(&refid.,dt_string))));
PROC SQL;
connect to db2 (user = xxxx database = xxxx password = xxxx);
execute (insert into xxxx.xxxx (var) values (TIMESTAMP(%str(%')&var.%str(%')))) by db2;
disconnect from db2;
quit;
%let refrc = %sysfunc(fetch(&refid.));
%end;
%let refid = %sysfunc(close(&refid.));
%mend;
%insert_timestamp;
EDIT: I guess you could also load the table as-is in DB2 using SAS/ACCESS and then convert the strings to timestamp with sql pass-through. Something like
libname lib db2 database=xxxx schema=xxxx user=xxxx password=xxxx;
data lib.temp;
set sas_table;
run;
PROC SQL;
connect to db2 (user = xxxx database = xxxx password = xxxx);
execute (create table xxxx.xxxx (var TIMESTAMP)) by db2;
execute (insert into xxxx.xxxx select TIMESTAMP(dt_string) from xxxx.temp) by db2;
execute (drop table xxxx.temp) by db2;
disconnect from db2;
quit;
I am trying to read a list of values into a macro, so that the macro variable would contain the table name and create a column that would contain the table name.
My attempt, which is wrong, was trying to use the code below, and erroring out because of the line " '&tbl' as Table_Dt ". The code below is inefficient, so feel free to enhance it. Thanks for your help.
%macro flat(tbl);
proc sql exec feedback stimer noprint outobs=5;
CREATE TABLE &tbl as
SELECT
ID,
DOB,
'&tbl' as Table_Dt
FROM &tbl..flat_file;
QUIT;
%mend flat;
%flat(flat0113);
%flat(flat0213);
...
%flat(flat1213);
As you are basically processing a list, this could also be done using call execute. No need to write all the information to macro variables. All tables/libraries are already stored in the sashelp tables and therefore are ready for list processing.
data _null_;
set sashelp.vslib (where=(substr(libname,1,4) = 'FLAT')) end =eof;
if _n_ = 1 then call execute ('proc sql exec feedback stimer noprint outobs=5;');
call execute ('
CREATE TABLE '|| libname ||' AS
SELECT ID,
DOB,
"'||compress(libname)||'" as Table_Dt
FROM '||compress(libname)||'.flat_file
;
');
if eof then call execute ('QUIT;');
run;
Macros in quotation marks will only resolve with double quotes, not single. If you want to do a more efficient way, you can do so with the following modified code. I am assuming that you are reading from libraries named flat0113, flat0213, etc.
Step 1: Get a list of all the libnames with the word "flat" in it
proc sql noprint;
select distinct libname
, count(libname)
into: tbl_list separated by ' '
, total_tbls
from sashelp.vmember
where libname LIKE 'FLAT%'
;
quit;
This will create two macro variables: &tbl_list, and &total_tbls.
&tbl_list holds the values flat0113 flat0213 flat ... flat1213.
&total_tbls holds the total number of values in &tbl_list.
Step 2: Loop through the newly created list
%macro readTables;
%do i = 1 %to &total_tbls;
%let tbl = %scan(tbl_list, &i);
proc sql exec feedback stimer noprint outobs=5;
CREATE TABLE &tbl as
SELECT
ID,
DOB,
"&tbl" as Table_Dt
FROM &tbl..flat_file;
quit;
%end;
%mend;
%readTables;
This will read each individual value from &tbl_list one by one until the very end of the list.
I'm trying to automate a job that involves a lot of data being passed across the network and between the actual db2 server and our SAS server. What I'd like to do is take a traditional pass through...
proc sql;
connect to db2(...);
create table temp as
select * from connection to db2(
select
date
.......
where
date between &start. and &stop.
); disconnect from db2;
quit;
into something like this:
x "db2 'insert into temp select date ...... where date between &start. and &stop.'";
I'm running into a few issues the first of which is db2 date format of 'ddMONyyyy'd which causes the shell command to terminate early. If I can get around that I think it should work.
I can pass a macro variable through to the AIX (SAS) server without the extra set of ' ' needed to execute the db2 command.
Any thoughts?
You might get around the single-quote around the date issue by setting off the WHERE clause with a parenthesis. I'm not sure that this will work, but it might be worth trying.
As far as X command, try something like the following:
%let start = '01jan2011'd;
%let stop = '31dec2011'd;
%let command_text = db2 %nrbquote(')insert into temp select date ... where (date between &start. and &stop.)%nrbquote(');
%put command_text = &command_text;
x "&command_text";