Teradata dynamic SQL getting code from another table - teradata

I am trying to recreate a query that uses a subquery in the left join to get the value of a KPI. I am trying to store that subquery in another table as a string and use dynamic sql to retrieve that string code and insert it into the left join, but I am getting an error:
[5526] SPL2010:E(L27), Variable of CHARACTER type expected in PREPARE or EXECUTE IMMEDIATE statement.
Here is the procedure I am trying to create. In the variable metric_code, the column SQL is a string that contains the sql code.
Create PROCEDURE dyn_sql_test()
BEGIN
DECLARE metric_code VARCHAR(5000);
Set metric_code = (Select SQL_CODE from mytable_with_Sql_code where ID=1234);
BEGIN
set get_value='
SELECT
e.*
,a.value
FROM Metric_table AS e
LEFT JOIN ('||metric_code||') a
on e.id=a.id
WHERE e.ID = 1234;';
EXECUTE IMMEDIATE get_value;
END;
END;
In case it helps, the string I am retrieving for SQL_CODE, which used to go in the Left Join as a subquery looks something like this:
sel id,type,values
from my_values_table
where type='kpi'

Related

Teradata Insert Count into Variable

Description what I am trying to do:
I have 2 environments one has data (X) second one has no data (Y).
I have done procedure which has input parameter P_TableName. It should check if in this table is any data and IF There is then we will take data to Y environment.
So Mostly it works but I have problem with one freaking simple thing ( I have not much experience in TD but in Oracle it would be a 10seconds).
I need to pass select count(*) from X to variable how to do that?.
I was trying by SET VAR = SELECT...
INSERT INTO VAR SELECT...
I was trying to make a variable for statement which is directly executing
SET v_sql_stmt = 'INSERT INTO ' || VAR|| ' SELECT COUNT(*) FROM ' || P_TableName;
CALL DBC.SYSEXECSQL(v_sql_stmt);
It's probably really simple thing but I can't find good solution for that. Please help
You'll have to open a cursor to fetch the results since you are running dynamic SQL. There is a good example in the Teradata help doc on Dynamic SQL:
CREATE PROCEDURE GetEmployeeSalary
(IN EmpName VARCHAR(100), OUT Salary DEC(10,2))
BEGIN
DECLARE SqlStr VARCHAR(1000);
DECLARE C1 CURSOR FOR S1;
SET SqlStr = 'SELECT Salary FROM EmployeeTable WHERE EmpName = ?';
PREPARE S1 FROM SqlStr;
OPEN C1 USING EmpName;
FETCH C1 INTO Salary;
CLOSE C1;
END;
You can't use INTO in Dynamic SQL in Teradata.
As a workaround you need to do a cursor returning a single row:
DECLARE cnt BIGINT;
DECLARE cnt_cursor CURSOR FOR S;
SET v_sql_stmt = ' SELECT COUNT(*) FROM ' || P_TableName;
PREPARE S FROM v_sql_stmt;
OPEN cnt_cursor;
FETCH cnt_cursor INTO cnt;
CLOSE cnt_cursor;

PLSQL FOR loop while executing CURSOR

I would like to know if there's any option to iterate a table while performing SELECT values into a CURSOR.
For example:
I have a table TEMP_NUMBERS which contains only numbers (single column).
I have to perform a SELECT from each number in the table (I do not know the amount of rows in the table in advance).
Here is basically what I'm attempting to do. Obviously this does not work, but can I do some kind of a workaround?
I need to SELECT the data into the p_cv_PermsNotifs which is a RETURN REF CURSOR.
IF NOT p_cv_PermsNotifs%ISOPEN THEN OPEN p_cv_PermsNotifs FOR
FOR i IN 1..TEMP_NUMBERS.NUMBER.COUNT LOOP
SELECT DISTINCT
SEC_USER_ROLE.ENTITY_TYP_CODE,
SEC_USER_ROLE.ENTITY_ID
FROM
SEC_USER_ROLE
WHERE
SEC_USER_ROLE.ENTITY_ID = i
END LOOP;
END IF;
Also tried this:
IF NOT p_cv_PermsNotifs%ISOPEN THEN OPEN p_cv_PermsNotifs FOR
SELECT DISTINCT
SEC_USER_ROLE.ENTITY_TYP_CODE,
SEC_USER_ROLE.ENTITY_ID
FROM
SEC_USER_ROLE
WHERE
SEC_USER_ROLE.ENTITY_ID IN
(SELECT * FROM TABLE (lv_ListOfEntities))
END IF;
Where lv_ListOfEntities is table of NUMBER indexed by BINARY INTEGER.
But I'm getting "ORA-22905: cannot access rows from a non-nested table item"
Thanks in advance.
In> Hey if you pass a single number at a time, everytime the refcursor
will be overwritten by the next value. So at the end you will only get
the value for last number in the refcursor. A better way is to use
some basic PL/SQL Bulk COLLECT logic which will give you the desired
output.
Hope this helps
--Creating sql type
CREATE OR REPLACE TYPE lv_num_tab IS TABLE OF NUMBER;
--plsql block
var p_lst refcursor;
DECLARE
lv_num lv_num_tab;
BEGIN
SELECT COL1 BULK COLLECT INTO lv_num FROM TEMP_NUMBERS;
OPEN p_lst FOR
SELECT DISTINCT SEC_USER_ROLE.ENTITY_TYP_CODE,
SEC_USER_ROLE.ENTITY_ID
FROM SEC_USER_ROLE
WHERE SEC_USER_ROLE.ENTITY_ID IN
(SELECT * FROM TABLE(cast(lv_num as lv_num_tab))
);
END;

create dynamic table within procedure?

I am trying to create dynamic table within procedure but i am getting error please
tell me whats the error
CREATE OR REPLACE PROCEDURE check_sms_bundle_25 (
MON VARCHAR2,
YEAR_P VARCHAR2 DEFAULT TO_CHAR (SYSDATE, 'YY'),
QUARTER VARCHAR2,
TYPE VARCHAR2 DEFAULT 'NEW')
IS
BEGIN
IF UPPER (QUARTER) = 1
THEN
EXECUTE IMMEDIATE 'BEGIN CREATE OR REPLACE TABLE (''SMS_Bundle_25_'''|| UPPER (MON)|| '''_Q'''|| UPPER (QUARTER)|| '||''_''||'''|| UPPER (YEAR_P)|| ''')
AS
SELECT customer_id, otxact
FROM ordertrailer INNER JOIN orderhdr_all ON ohxact = otxact
WHERE sncode = 343 AND ohentdate = ''1-aug-2014'' AND ohstatus = ''IN''
END';
END IF;
END;
The error msg is
ORA-06550: line 2, column 4: PLS-00103: Encountered the symbol
"CREATE" when expecting one of the following:
begin case declare exit for goto if loop mod null pragma raise
return select update while with << close current delete
fetch lock insert open rollback savepoint set sql execute commit
forall merge pipe ORA-06512: at "FI_SDINE.CHECK_SMS_BUNDLE_25", line 9
ORA-06512: at line 1
As well as 'or replace' not being valid as part of the create table syntax, you're enclosing the DDL statement inside another anonymous PL/SQL block, you're trying to put the table name inside parentheses, and you're including quote marks - which are not allowed in an (unquoted) object identifier. So if you pass in argukments AUG, 14, 1 NEW then your dynamic statement is trying to run:
BEGIN CREATE OR REPLACE TABLE ('SMS_Bundle_25_'AUG'_Q'1||'_'||'14')
AS
SELECT customer_id, otxact
FROM ordertrailer INNER JOIN orderhdr_all ON ohxact = otxact
WHERE sncode = 343 AND ohentdate = '1-aug-2014' AND ohstatus = 'IN'
END
The BEGIN/END make is a block and the DDL statement isn't valid in PL/SQL; that's why you're having to use execute immediate in the first place. If you change your construction to:
EXECUTE IMMEDIATE 'CREATE TABLE SMS_Bundle_25_'|| UPPER (MON)
|| '_Q'|| UPPER (QUARTER) ||'_' || UPPER (YEAR_P)|| '
AS
SELECT customer_id, otxact
FROM ordertrailer INNER JOIN orderhdr_all ON ohxact = otxact
WHERE sncode = 343 AND ohentdate = ''1-aug-2014'' AND ohstatus = ''IN''');
you'd then be trying to run:
CREATE TABLE SMS_Bundle_25_AUG_Q1_14
AS
SELECT customer_id, otxact
FROM ordertrailer INNER JOIN orderhdr_all ON ohxact = otxact
WHERE sncode = 343 AND ohentdate = '1-aug-2014' AND ohstatus = 'IN'
which at least looks more viable, assuming the tables you're selecting from exist. No idea why you're passing the year and quarter numbers as strings or applying upper() to them. And presumably you really want the select to be filtered on the same year and month as the table name.
It's useful to display the command you're trying to execute, for example with dbms_output, to see exactly what you've created; and you can then also run that manually to see where it's going wrong more clearly.
But as noted in comments, it's unusual to create objects from a procedure like this. Your schema should usually be static, not modified on the fly. You won't be able to refer to this table from other code unless that is also being built dynamically. You seem to be creating a table as a snapshot of the other tables; a view or a materialised view might be more appropriate. But I'm not really sure why you're doing this so it's not entirely clear what you should be doing instead.

Using nvarchar(MAX) to build query, but conversion fails in where clause

I have a stored procedure that uses a variable called #Command (nvarchar(MAX)). I then add parameters accordingly based on given input.
declare #Command nvarchar(max)
if(#CaseFileID IS NOT NULL)
BEGIN
select #Command='
select [ServerCredentialsID],[CaseFileID],EIKSLT.[LocationType],EPT.PaymentType,[TaskID],[DateActive]
,[LengthOfPurchase],[Username],[Password],[IPDomain],[Port],[DES],[Website],[AmountPaid],[Latitude]
,[Longitude],[HasAttachments],[TimeStamp],[CaseElement],[Temporary],[StatusID]
FROM Element17a_IKSServerCredentials EIKSSC
JOIN ElementsIKSLocationTypes EIKSLT ON EIKSSC.LocationBeingUsedID= EIKSLT.IKSLocationBeingUsedID
JOIN ElementsPaymentTypes EPT ON EIKSSC.PaymentMethodID=EPT.PaymentTypeID
where EIKSSC.CaseFileID='''+cast(#CaseFileID as nvarchar(MAX))+''' '
#CaseFileID is declared as an int, and in the table it is an int. When I try
where EIKSSC.CaseFileID = ' + #CaseFileID + ' '
then the value doesn't even show (in the error it looks like "EIKSSC.CaseFileID= '" )
I just don't get it.
NOTE: SQL Server 2008 Management Studio
It's because #CaseFileID is VARCHAR even though you don't show it.
Your IF should be
if(#CaseFileID > '')
And if even that doesn't work, then you need to swap to LEFT joins because INNER JOINs will remove records that cannot be matched in the other 2 tables.
Finally, because CaseFileID is an int, you don't need the quotes. Even though SQL Server will implicitly cast '9' to the integer 9 in the WHERE clause, it's just not necessary.
declare #Command nvarchar(max)
if(#CaseFileID > '')
BEGIN
select #Command='
select [ServerCredentialsID],[CaseFileID],EIKSLT.[LocationType],EPT.PaymentType,[TaskID],[DateActive]
,[LengthOfPurchase],[Username],[Password],[IPDomain],[Port],[DES],[Website],[AmountPaid],[Latitude]
,[Longitude],[HasAttachments],[TimeStamp],[CaseElement],[Temporary],[StatusID]
FROM Element17a_IKSServerCredentials EIKSSC
LEFT JOIN ElementsIKSLocationTypes EIKSLT ON EIKSSC.LocationBeingUsedID= EIKSLT.IKSLocationBeingUsedID
LEFT JOIN ElementsPaymentTypes EPT ON EIKSSC.PaymentMethodID=EPT.PaymentTypeID
where EIKSSC.CaseFileID='+cast(#CaseFileID as nvarchar(MAX))

dynamic table name in a query

How can I put a table name dynamically in a query?
Suppose I have a query as shown below:
Select a.amount
,b.sal
,a.name
,b.address
from alloc a
,part b
where a.id=b.id;
In the above query I want to use a table dynamically (part b if the database is internal, p_part b if the database if external).
I have a function that returns which database it is. Suppose the function is getdatabase();
select decode(getdatabase(),'internal','part b','external','p_part b')
from dual;
How can I use this function in my main query to insert the table name dynamically into the query?
I don't want to implement this using the primitive way of by appending strings to make a final query and then open cursor with that string.
I don't want to implement this with primitive way of by appending
strings to make a final query and then open cursor with that string .
That's really the only way you can do it. It's not possible to use a variable or function call for the table name when using a regular PL/SQL SQL block, you have to use dynamic SQL.
Refer to Oracle documentation for more details:
http://docs.oracle.com/cd/B10500_01/appdev.920/a96590/adg09dyn.htm
Here's an example from the doc:
EXECUTE IMMEDIATE 'SELECT d.id, e.name
FROM dept_new d, TABLE(d.emps) e -- not allowed in static SQL
-- in PL/SQL
WHERE e.id = 1'
INTO deptid, ename;
You can do this without dynamic SQL, assuming both tables (part and p_part) are available at compile time:
select a.amount
,b.sal
,a.name
,b.address
from alloc a
,part b
where a.id=b.id
and (select getdatabase() from dual) = 'internal'
UNION ALL
select a.amount
,b.sal
,a.name
,b.address
from alloc a
,p_part b
where a.id=b.id
and (select getdatabase() from dual) = 'external'
;
I've put the function call in a subquery so that it is run only once per call (i.e. twice, in this instance).

Resources