Teradata Stored Procedure variables - sql

I'm trying to create a stored procedure to create all possible combination of a table with itself. For now, I got this code, but it produces the following error:
Syntax error: expected something between the word 'A' and the integer '2'
Code:
CREATE MULTISET TABLE PRUEBA
(
CAMPO VARCHAR(10)
);
INSERT INTO PRUEBA VALUES('A');
INSERT INTO PRUEBA VALUES('B');
INSERT INTO PRUEBA VALUES('C');
REPLACE PROCEDURE TEST()
BEGIN
DECLARE a VARCHAR(255);
DECLARE b VARCHAR(225);
DECLARE qry VARCHAR(255);
DECLARE i INT;
DECLARE n INT;
SET a = 'SELECT * FROM PRUEBA A1 ';
SET b = ' WHERE ';
SET n = 3;
SET i = 1;
WHILE i < n DO
BEGIN
CASE i
WHEN 1 THEN
SET qry = a;
WHEN 2 THEN
SET a = a || 'CROSS JOIN PRUEBA A' || i ; -- Error in this part.
SET b = b || 'A' || (i-1) || '.CAMPO < A' || i || '.CAMPO';
SET qry = a || b;
ELSE
SET a = a || 'CROSS JOIN PRUEBA A' || i ;
SET b = b || 'AND A' || (i-1) || '.CAMPO < A' || i || '.CAMPO';
SET qry = a || b;
END CASE;
SET i = i + 1;
END;
END WHILE;
EXECUTE IMMEDIATE qry;
END;
CALL TEST();
I'd join the 'i' variable to create multiple alias for all cross tables.

Your i variable is an INTEGER. Try casting it as a VARCHAR() when you do your concatenations:
SET a = a || 'CROSS JOIN PRUEBA A' || CAST(i AS VARCHAR(2)) ; -- Error in this part.
SET b = b || 'A' || CAST((i-1) AS VARCHAR(2)) || '.CAMPO < A' ||
CAST(i AS VARCHAR(2)) || '.CAMPO';
You'll have to do this in the subsequent ELSE block as well.

Use explicit CAST or TRIM to avoid the leading blanks generated by implicit cast from INTEGER to VARCHAR. And you need to use a dynamic cursor to return the result of the SELECT to the caller.
REPLACE PROCEDURE TEST()
DYNAMIC RESULT SETS 1 --Allow returning data to caller
BEGIN
DECLARE a VARCHAR(255);
DECLARE b VARCHAR(225);
DECLARE qry VARCHAR(4096);
DECLARE i INT;
DECLARE n INT;
DECLARE csr1 CURSOR WITH RETURN FOR stmt1; --Declare a dynamic cursor
SET a = 'SELECT * FROM PRUEBA A1 ';
SET b = ' WHERE ';
SET n = 3;
SET i = 1;
WHILE i < n DO
BEGIN
CASE i
WHEN 1 THEN
SET qry = a;
WHEN 2 THEN
SET a = a || 'CROSS JOIN PRUEBA A' || TRIM(i) ;
SET b = b || 'A' || TRIM(i-1) || '.CAMPO < A' || TRIM(i) || '.CAMPO';
SET qry = a || b;
ELSE
SET a = a || 'CROSS JOIN PRUEBA A' || TRIM(i) ;
SET b = b || 'AND A' || TRIM(i-1) || '.CAMPO < A' || TRIM(i) || '.CAMPO';
SET qry = a || b;
END CASE;
SET i = i + 1;
END;
END WHILE;
PREPARE stmt1 FROM qry; --Prepare a dynamic SQL statement for the cursor
OPEN csr1; --Execute the SELECT statement
--Leave a WITH RETURN cursor open to return the result set
END;

if you're using bteq you might have to write the procedure into a file and load it with .compile directive.
once you are loggen in and supposing the file is /tmp/stored_procedure.sql
compile it like this:
.compile file='/tmp/stored_procedure.sql';

Related

Update command not working inside nested for loop postgres

I have nested for loop as shown below and trying to perform the update statement, but the update command is not working
CREATE or REPLACE FUNCTION public.udf_unit_test_output() RETURNS SETOF text AS
$$
DECLARE
v_expected_proc_name varchar(100);
v_expected_log_severity varchar(25);
v_expected_log_text varchar(8000);
v_expected_occurence int;
v_actual_occurence int;
record_list record;
distinct_unit_test_uuid uuid;
BEGIN
for distinct_unit_test_uuid in
SELECT DISTINCT unit_test_uuid FROM public.unit_test_expected_results
loop
for record_list in select record_id
from public.unit_test_expected_results
WHERE unit_test_uuid = distinct_unit_test_uuid
loop
SELECT proc_name, log_severity, log_text, occurence
FROM public.unit_test_expected_results
WHERE record_id = record_list.record_id
INTO v_expected_proc_name,
v_expected_log_severity,
v_expected_log_text,
v_expected_occurence;
SELECT COUNT(*)
FROM public.unit_test_output uto
WHERE unit_test_uuid = distinct_unit_test_uuid
AND proc_name = v_expected_proc_name
AND log_severity = v_expected_log_severity
AND log_text like v_expected_log_text
INTO v_actual_occurence;
UPDATE public.unit_test_expected_results
SET pass_fail = (CASE WHEN v_expected_occurence = v_actual_occurence
THEN 'PASS' ELSE 'FAIL'
END)
WHERE record_id = record_list.record_id;
RETURN QUERY SELECT is(v_expected_occurence, v_actual_occurence,
'Should contain '||'proc name: ' || v_expected_proc_name || ' severity type: '
|| v_expected_log_severity || ' and log text: ' || v_expected_log_text);
end loop;
end loop;
RETURN;
END;
$$
LANGUAGE plpgsql;
It doesn't update any of the row ?

How can i turn this pl/sql into a procedure

I had to write this query for an assignement. So we have a database and we are pulling information from it, this is going to work with some back end c# eventually. Is there anything i can do , knowing im going to reuse this, in order to make it better and more adaptable when the day comes when i have to connect it all.
set serveroutput on
DECLARE
LV_DATE HVK_RESERVATION.RESERVATION_START_DATE%TYPE;
LV_SERV VARCHAR(100);
CURSOR LCUR_RES IS
SELECT *
FROM HVK_RESERVATION R
INNER JOIN HVK_PET_RESERVATION PR
ON R.RESERVATION_NUMBER = PR.RES_RESERVATION_NUMBER
INNER JOIN HVK_PET P
ON P.PET_NUMBER = PR.PET_PET_NUMBER
INNER JOIN HVK_OWNER OW
ON OW.OWNER_NUMBER = P.OWN_OWNER_NUMBER
WHERE R.RESERVATION_START_DATE < LV_DATE
AND R.RESERVATION_END_DATE > LV_DATE;
CURSOR LCUR_SERVICE(PET_RES_NUM NUMBER) IS
SELECT *
FROM HVK_SERVICE S
INNER JOIN HVK_PET_RESERVATION_SERVICE PRS
ON PRS.SERV_SERVICE_NUMBER = S.SERVICE_NUMBER
AND PRS.PR_PET_RES_NUMBER = PET_RES_NUM;
BEGIN
LV_DATE := TO_DATE('&logdate', 'yy-mm-dd');
DBMS_OUTPUT.PUT_LINE('Kennel log for ' || '' || LV_DATE);
DBMS_OUTPUT.PUT_LINE('-------------------------------');
FOR I IN LCUR_RES LOOP
DBMS_OUTPUT.PUT_LINE('Run:' || '' || I.RUN_RUN_NUMBER || ' ' ||
'Pet: ' || '' || I.PET_NAME || ' ' ||
I.OWNER_LAST_NAME || ' Pet Reservation: ' || '' ||
I.PET_RES_NUMBER);
DBMS_OUTPUT.PUT_LINE('Reservation start/end ' || ' ' ||
I.RESERVATION_START_DATE || ' ' ||
I.RESERVATION_END_DATE);
DBMS_OUTPUT.PUT('Services : ');
FOR X IN LCUR_SERVICE(I.PET_RES_NUMBER) LOOP
DBMS_OUTPUT.PUT(X.SERVICE_DESCRIPTION || ' ');
END LOOP;
DBMS_OUTPUT.PUT_LINE('');
FOR LREC_LOG IN (SELECT *
FROM HVK_KENNEL_LOG KL
WHERE KL.PR_PET_RES_NUMBER = I.PET_RES_NUMBER
) LOOP
DBMS_OUTPUT.PUT_LINE('Notes: ' || '' ||
LREC_LOG.KENNEL_LOG_SEQUENCE_NUMBER || ' ' ||
'Log Note: ' || '' || LREC_LOG.KENNEL_LOG_NOTES);
END LOOP;
DBMS_OUTPUT.PUT_LINE(' ');
END LOOP;
END;
It it supposed to output the run number , reservation number , pet name , and any relate notes.
you can replace DECLARE with CREATE OR REPLACE PROCEDURE my_proc(in_logdate in date) IS.
in that case my_proc will be the name of your procedure.
you should also use a parameter instead of &logdate
so e.g. parameter name in_logdate of type date
...
LV_DATE := in_logdate;
...

Getting 'ORA00904: invalid identifier' on dynamic tables

I have a script that works with dynamic tables. When executing the below code segment, it gives me error ORA00904: invalid identifier.
IF Database_SYS.Column_Exist (service_tab_, ''KEY_VALUE'') THEN
UPDATE '|| service_tab_ || '
SET key_ref = new_key_ref_,
key_value = ''Test'',
rowversion = SYSDATE
WHERE ROWID = rec_.ROWID;
ELSE
UPDATE '|| service_tab_ || '
SET key_ref = new_key_ref_,
rowversion = SYSDATE
WHERE ROWID = rec_.ROWID;
END IF;
Would be better this one:
BEGIN
IF Database_SYS.Column_Exist (service_tab_, '''' || KEY_VALUE || '''') THEN
EXECUTE IMMEDIATE
'UPDATE ' || service_tab_ || '
SET key_ref = new_key_ref_,
key_value = :val,
rowversion = SYSDATE
WHERE ROWID = :rid'
USING 'TEST', rec_.ROWID;
ELSE
EXECUTE IMMEDIATE
'UPDATE ' || service_tab_ || '
SET key_ref = new_key_ref_,
rowversion = SYSDATE
WHERE ROWID = :rid'
USING rec_.ROWID;
END IF;
END;
I don't think you need '''' || KEY_VALUE || '''', using just Database_SYS.Column_Exist(service_tab_, KEY_VALUE) should be fine if you have properly coded the function.
Try something like this:
EXECUTE IMMEDIATE 'update' || service_tab_ || 'SET key_ref = ' ||new_key_ref_|| ' rowversion = SYSDATE WHERE ROWID = '|| rec_.ROWID;
LEt me know if you get further errors, we can resolve them together in comments.
BEGIN
IF Database_SYS.Column_Exist (service_tab_, '''' || KEY_VALUE || '''') -- 4x': StartString + Quote + ' + EndString doublepipe to concat Strings
THEN
EXECUTE IMMEDIATE -- If you want to Build dynamic SQL you have to throw it into "EXECUTE IMMEDIATE 'myQuery'"
'UPDATE ' || service_tab_ || '
SET key_ref = new_key_ref_,
key_value = ''TEST'',
rowversion = SYSDATE
WHERE ROWID = rec_.ROWID';
ELSE
EXECUTE IMMEDIATE
'UPDATE ' || service_tab_ || '
SET key_ref = new_key_ref_,
rowversion = SYSDATE
WHERE ROWID = rec_.ROWID';
END IF;
END;
Next time you should Build up your script step-by-step. A single Error can be handled better than having a bunch of invalid code..
If the only difference is in updating a column it would be more correct to apply the condition only to a part of the query
DECLARE
V_QUERY VARCHAR2(200);
BEGIN
--
V_QUERY := 'UPDATE '||service_tab_||
' SET key_ref = new_key_ref_,
rowversion = SYSDATE';
--
IF Database_SYS.Column_Exist (service_tab_, 'KEY_VALUE') THEN
V_QUERY := V_QUERY||', key_value = ''Test''';
END IF;
--
V_QUERY := V_QUERY||' WHERE ROWID = rec_.ROWID';
--
EXECUTE IMMEDIATE V_QUERY;
--
END;

WHILE loop in Teradata procedure

I'm trying to write a procedure that concatenates all rows in a table in the case in which the row number is unknown.
I have this code but it is not working.
CREATE PROCEDURE Test (OUT r VARCHAR(3000))
BEGIN
DECLARE RowCnt INT;
DECLARE CurrRow INT ;
SET CurrRow = 1,
r = 'SELECT ',
RowCnt = (SELECT COUNT(*)
FROM tableWithSQLStmnts
)
WHILE CurrRow <= RowCnt DO
BEGIN
SET r = r +
CASE WHEN CurrRow = 1
THEN 'MAX( CASE Seq WHEN ' + CAST( CurrRow AS VARCHAR ) + '
THEN SqlStmnt
ELSE SPACE(0) END ) + ' + CHAR(13)
WHEN i = RowCnt
THEN 'MAX( CASE Seq WHEN ' + CAST( CurrRow AS VARCHAR ) + '
THEN '' '' + SqlStmnt
ELSE SPACE(0) END ) ' + CHAR(13)
ELSE 'MAX( CASE Seq WHEN ' + CAST( CurrRow AS VARCHAR ) + '
THEN '' '' + SqlStmnt
ELSE SPACE(0) END ) + ' + CHAR(13)
END
SET CurrRow = CurrRow + 1 ;
END ;
SET r = r + '
FROM ( SELECT SqlStmnt,
ROW_NUMBER() OVER ( PARTITION BY TabName ORDER BY SQlStmnt )
FROM tableWithSQLStmnts t ) D ( SqlStmnt, Seq )
GROUP BY TabName;'
END WHILE;
END
;
I'm getting the following errors:
Syntax error, expected something like ';' between an integer and ','.'.
Unexpected text 'SET'.
New code, as suggested by dnoeth.
REPLACE PROCEDURE Test3 (IN TbName VARCHAR(256)) --, OUT r2 VARCHAR(3000))
BEGIN
DECLARE RowCnt INT;
DECLARE i INT;
DECLARE CurrRow INT;
DECLARE r VARCHAR(3000);
DECLARE r2 VARCHAR(3000);
SET CurrRow = 1;
SET r = 'SELECT ';
SET RowCnt = (SELECT COUNT(*)
FROM tableWithSQLStmnts
WHERE tabname = :TbName
);
WHILE CurrRow <= RowCnt DO
BEGIN
SET r = r ||
'MAX( CASE Seq WHEN ' || CAST( CurrRow AS VARCHAR(10) ) || '
THEN '' , '' || SqlStmnt
ELSE '''' END )
'
|| CASE WHEN CurrRow = RowCnt
THEN ''
ELSE ' || '
END;
SET CurrRow = CurrRow + 1 ;
END;
END WHILE;
SET r = r || '
FROM ( SELECT SqlStmnt,
ROW_NUMBER() OVER ( PARTITION BY TbName ORDER BY SQlStmnt )
FROM tableWithSQLStmnts t ) D ( SqlStmnt )
GROUP BY TbName
;';
SET r2 = r;
CALL dbc.sysexecsql(:r);
END;
Now I get this error:
[3706] Syntax error: Column name list shorter than select list.
EDIT 2:
I have now rewritten it like this:
REPLACE PROCEDURE Test3 (IN TabName VARCHAR(256))
DYNAMIC RESULT SETS 1
BEGIN
DECLARE RowCnt INT;
DECLARE Seq INT;
DECLARE QRY VARCHAR(3000);
DECLARE CurrRow INT;
SET QRY= 'INSERT INTO vt21 SELECT ';
SET CurrRow = 1;
CREATE VOLATILE TABLE vt21(QRY VARCHAR(3000)) ON COMMIT PRESERVE ROWS;
SET RowCnt = (SELECT COUNT(*)
FROM TestTable
WHERE tabname = :TabName
);
FOR CurrentRefRow AS SourceCursor CURSOR FOR
SELECT SqlStmnt
FROM TestTable
DO
WHILE CurrRow <= RowCnt
DO
BEGIN
SET QRY = QRY ||
CASE WHEN CurrRow=1
THEN 'MAX( CASE Seq WHEN ' || CAST( CurrRow AS VARCHAR(10) ) || '
THEN '' , '' || SqlStmnt
ELSE '''' END ) '
WHEN CurrRow < RowCnt
THEN ', MAX( CASE Seq WHEN ' || CAST( CurrRow AS VARCHAR(10) ) || '
THEN '' , '' || SqlStmnt
ELSE '''' END ) '
WHEN CurrRow=RowCnt
THEN ', MAX( CASE Seq WHEN ' || CAST( CurrRow AS VARCHAR(10) ) || '
THEN '' , '' || SqlStmnt
ELSE '''' END ) '
ELSE ' || '
END;
SET CurrRow = CurrRow + 1 ;
END;
END WHILE;
SET QRY = QRY || '
FROM ( SELECT SqlStmnt, Tabname,
ROW_NUMBER() OVER ( PARTITION BY TabName ORDER BY SQlStmnt )
FROM TestTable t ) D ( Seq, Tabname, SqlStmnt )
GROUP BY TabName
;';
EXECUTE IMMEDIATE QRY;
END FOR;
BEGIN -- return the result set
DECLARE resultset CURSOR WITH RETURN ONLY FOR S1;
SET QRY = 'SELECT * FROM vt21;';
PREPARE S1 FROM QRY;
OPEN resultset;
END;
DROP TABLE vt21;
END;
But I'm getting the following error:
CALL Failed. [3813] The positional assignment list has too many values.
I have tried modifying it but when I delete one value than it says that column name list is longer then the select list.
This is translated to valid syntax for Teradata/Standard SQL (and a bit simplified):
REPLACE PROCEDURE Test (OUT r2 VARCHAR(3000))
BEGIN
DECLARE RowCnt INT;
DECLARE i INT;
DECLARE CurrRow INT;
DECLARE r VARCHAR(3000);
SET CurrRow = 1;
SET r = 'SELECT ';
SET RowCnt = (SELECT Count(*)
FROM tableWithSQLStmnts
);
WHILE CurrRow <= RowCnt DO
BEGIN
SET r = r ||
'MAX( CASE Seq WHEN ' || Cast( CurrRow AS VARCHAR(10) ) || '
THEN '' '' || SqlStmnt
ELSE '''' END )
'
|| CASE WHEN CurrRow = RowCnt
THEN ''
ELSE ' || '
END;
SET CurrRow = CurrRow + 1 ;
END;
END WHILE;
SET r = r || '
FROM ( SELECT department_name--SqlStmnt,
ROW_NUMBER() OVER ( PARTITION BY TabName ORDER BY SQlStmnt )
FROM tableWithSQLStmnts t ) D ( SqlStmnt, Seq )
GROUP BY TabName
;';
SET r2 = r;
END
;
What's the content of tableWithSQLStmnts?
Why do you want a single line? There are simpler ways to get a kind of LISTAGG.
Edit:
Based on your comments (here and on Teradata's Developer Exchange) it looks like you want to apply some kind of count to every column. But then you don't need the MAX/CASE/ROW_NUMBER, simply concat all rows for a table and then execute it. This counts NULLs in every column of a table:
REPLACE PROCEDURE Test3 (IN DBName VARCHAR(128),IN TabName VARCHAR(128))
DYNAMIC RESULT SETS 1
BEGIN
DECLARE QRY VARCHAR(3000);
CREATE VOLATILE TABLE vt21(col VARCHAR(128) CHARACTER SET Unicode, NullCnt BIGINT) ON COMMIT PRESERVE ROWS;
SET QRY = 'INSERT INTO vt21 ';
FOR c AS
SELECT DatabaseName, TableName, ColumnName,
Row_Number()
Over (PARTITION BY tablename
ORDER BY columnname) AS rn,
Count(*)
Over (PARTITION BY tablename) AS Cnt
FROM dbc.ColumnsV
WHERE DatabaseName = :DBName
AND TableName = :TabName
DO
SET QRY = QRY
|| 'SELECT ''' || c.ColumnName
|| ''', COUNT(CASE WHEN ' || c.columnname
|| ' IS NULL THEN 1 END) FROM '
|| c.DatabaseName || '.' || c.TableName
|| CASE WHEN c.rn = c.Cnt -- last row
THEN ';'
ELSE ' UNION ALL '
END;
END FOR;
EXECUTE IMMEDIATE QRY;
BEGIN -- return the result set
DECLARE resultset CURSOR WITH RETURN ONLY FOR S1;
SET QRY = 'SELECT * FROM vt21;';
PREPARE S1 FROM QRY;
OPEN resultset;
END;
DROP TABLE vt21;
END;
CALL Test3('dbc', 'dbcinfoV');

Oracle SQL - Alternative for dynamic query

I have a procedure (see below) which is using dynamic query. I wanted to rewrite this procedure with out using dynamic query. How do I write the conditions below?
PROCEDURE DemoProcedure(p_firstname IN VARCHAR2,
p_lastname IN VARCHAR2,
p_phone IN VARCHAR2
o_Cursor OUT t_Cursor) IS
SQLString VARCHAR2(4000);
BEGIN
SQLString :=
'SELECT * FROM
SCHEMA.TABLENAME_a A
INNER JOIN SCHEMA.TABLENAME_b B
ON A.ID = B.ID
WHERE
A.TYPE = 1 ';
IF p_firstname IS NOT NULL THEN
SQLString := SQLString || ' and UPPER(A.FIRST_NAME) like UPPER( ''' || p_firstname || ''')';
END IF;
IF p_lastname IS NOT NULL THEN
SQLString := SQLString || ' and UPPER(A.LAST_NAME) like UPPER( ''' || p_lastname || ''')';
END IF;
IF p_phone IS NOT NULL THEN
SQLString := SQLString || ' and UPPER(A.PHONE) = ''' ||
p_phone || '''';
END IF;
SQLString := SQLString || ' order by a.id ';
OPEN o_Cursor FOR SQLString;
END DemoProcedure;
It looks like you just want
OPEN o_cursor
FOR SELECT ...
WHERE A.TYPE = 1
AND (p_firstname IS NULL or upper(a.first_name) = upper(p_firstname))
AND (p_lastname IS NULL or upper(a.last_name) = upper(p_lastname))
AND (p_phone IS NULL or upper(a.phone) = p_phone)
ORDER BY a.id
I'm not sure why you'd want to bother upper-casing a phone number-- do you have character data in a phone number?
Different way writing the same SQL as #JustinCave suggested -
OPEN o_cursor
FOR SELECT ...
WHERE A.TYPE = 1
AND upper(a.first_name) = nvl(upper(p_firstname), upper(a.first_name))
AND upper(a.last_name) = nvl(upper(p_lastname),upper(a.last_name))
AND upper(a.phone) = nvl(p_phone,upper(a.phone))
ORDER BY a.id