Cursor declaration in Amazon Redshift - sql-update

My aim is to parse the first table check if the ID exists in the second table, then do some updates.
The first table runs in loop and parses each row in the second table to update or give the output.
Here the code that I used:
DECLARE
CURSOR c1 FOR (SELECT pnr,agrnumber,pnrcreatedate FROM test2_view_table);
r1 c1%ROWTYPE;
CURSOR c2 FOR (SELECT pnr,ano,pcdt FROM sdh_ticket_test2_update);
r2 c2%ROWTYPE;
BEGIN
FOR r1 IN c1
LOOP
FOR r2 IN c2
LOOP
IF (r1.pnr = r2.pnr and r2.pnrcreatedate is null and
r1.agrnumber=r2.ano)
THEN
c++
else if (r1.pnr = r2.pnr and r2.agrnumber is null and
r1.pcdt=r2.pnrcreatedate)
THEN
a++ -- continue to the next iteration of the outer loop
-- as we have a match
GOTO continue;
END IF;
END LOOP;
-- we can only reach that point if there was no match
DBMS_OUTPUT.PUT_LINE(TO_CHAR(c));
<<continue>>
NULL;
END LOOP;
END;
For the declaration of cursor in redshift as above. its throwing me the following error.
[Amazon](500310) Invalid operation: syntax error at or near "c1"
Please let me know if there is any solution.

This kind of row-by-row processing is not suitable for any modern database. Just define the logic in a SQL query (or queries) and let the DB figure out how to do the actual work.
In this case you can do the entire comparison in one step. You could actually rewrite this as a single UPDATE.
BEGIN
UPDATE sdh_ticket_test2_update
SET pnrcreatedate = c1.pcdt
FROM test2_view_table c1
LEFT JOIN sdh_ticket_test2_update c2
ON c1.pnr = c2.pnr
AND c1.agrnumber = c2.ano
WHERE c2.pnrcreatedate IS NULL
;
UPDATE sdh_ticket_test2_update
SET ano = c1.agrnumber
FROM test2_view_table c1
LEFT JOIN sdh_ticket_test2_update c2
ON c1.pnr = c2.pnr
AND c1.pcdt = c2.pnrcreatedate
WHERE c2.agrnumber IS NULL
;
COMMIT

Related

How to use string as column name in Bigquery

There is a scenario where I receive a string to the bigquery function and need to use it as a column name.
here is the function
CREATE OR REPLACE FUNCTION METADATA.GET_VALUE(column STRING, row_number int64) AS (
(SELECT column from WORK.temp WHERE rownumber = row_number)
);
When I call this function as select METADATA.GET_VALUE("TXCAMP10",149); I get the value as TXCAMP10 so we can say that it is processed as SELECT "TXCAMP10" from WORK.temp WHERE rownumber = 149 but I need it as SELECT TXCAMP10 from WORK.temp WHERE rownumber = 149 which will return some value from temp table lets suppose the value as A
so ultimately I need value A instead of column name i.e. TXCAMP10.
I tried using execute immediate like execute immediate("SELECT" || column || "from WORK.temp WHERE rownumber =" ||row_number) from this stack overflow post to resolve this issue but turns out I can't use it in a function.
How do I achieve required result?
I don't think you can achieve this result with the help of UDF in standard SQL in BigQuery.
But it is possible to do this with stored procedures in BigQuery and EXECUTE IMMEDIATE statement. Consider this code, which simulates the situation you have:
create or replace table d1.temp(
c1 int64,
c2 int64
);
insert into d1.temp values (1, 1), (2, 2);
create or replace procedure d1.GET_VALUE(column STRING, row_number int64, out result int64)
BEGIN
EXECUTE IMMEDIATE 'SELECT ' || column || ' from d1.temp where c2 = ?' into result using row_number;
END;
BEGIN
DECLARE result_c1 INT64;
call d1.GET_VALUE("c1", 1, result_c1);
select result_c1;
END;
After some research and trial-error methods, I used this workaround to solve this issue. It may not be the best solution when you have too many columns but it surely works.
CREATE OR REPLACE FUNCTION METADATA.GET_VALUE(column STRING, row_number int64) AS (
(SELECT case
when column_name = 'a' then a
when column_name = 'b' then b
when column_name = 'c' then c
when column_name = 'd' then d
when column_name = 'e' then e
end from WORK.temp WHERE rownumber = row_number)
);
And this gives the required results.
Point to note: the number of columns you use in the case statement should be of the same datatype else it won't work

How to compare two variables in IF statement?

I am trying to execute the below query.
I am trying to do this comparison using IF statement. I am expecting my result to like the values which are not in v_anwen_id should be selected from v_anwen2_id. so when v_anwen2_id is not equal to v_anwen_id then to execute the insert statement. But i am not getting the " v_anwen2_id is not equal to v_anwen_id " value. ( i wanted it to be like when a is not equal to b then execute insert statement. but still it is inserting the values which are in b as well).
I am not sure if it is the problem with my if statement or with the cursor.
IF v_anwen2_id <> v_anwen_id
Below is my query.
SET serveroutput ON;
DECLARE
v_client_id NUMBER(38);
v_roles_id NUMBER(38);
v_old_roles NUMBER(38);
v_rights_id NUMBER(38);
v_user1_id NUMBER(38);
v_anwen_id NUMBER(38);
v_anwen2_id NUMBER(38);
v_client1_id NUMBER(38);
v_abtei NUMBER(38);
CURSOR c_abtei (var01 NUMBER) IS
SELECT abteil,client_id FROM ty_join_anwen_abteil WHERE anwen = var01;
CURSOR c_anwen (var02 NUMBER) IS
SELECT anwen FROM ty_join_anwen_abteil WHERE abteil = var02;
CURSOR c_roles (var03 NUMBER) IS
SELECT roles FROM ty_join_rolesn_righte WHERE right = var03;
CURSOR c_users (var04 NUMBER) IS
SELECT anwen FROM ty_join_anwen_rolesn WHERE roles = var04;
BEGIN
-- SELECT id INTO v_roles_id FROM ty_sd_roles WHERE description ='cod_Test';
SELECT id INTO v_rights_id FROM ty_sd_berightigung WHERE description ='failure_note';
-- INSERT INTO ty_join_rolesn_righte (role,right)
-- VALUES (v_roles_id,v_rights_id);
SELECT ID INTO v_user1_id FROM ty_sd_person WHERE email = 'jens#gmail.com';
OPEN c_abteil(v_user1_id);
LOOP
FETCH c_abteil INTO v_abteil,v_client1_id;
EXIT WHEN c_abteil%NOTFOUND;
OPEN c_anwen(v_abteil);
LOOP
FETCH c_anwen INTO v_anwen_id;
EXIT WHEN c_anwen%NOTFOUND;
-- INSERT INTO ty_join_anwen_rolesn (anwen,roles,client_id) VALUES (v_anwen_id,v_roles_id,v_client1_id);
END LOOP;
CLOSE c_anwen;
END LOOP;
CLOSE c_abteil;
OPEN c_roles(v_rights_id);
LOOP
FETCH c_roles INTO v_old_roles;
EXIT WHEN c_roles%NOTFOUND;
OPEN c_users(v_old_roles);
LOOP
FETCH c_users INTO v_anwen2_id;
EXIT WHEN c_users%NOTFOUND;
IF v_anwen2_id <> v_anwen_id
THEN
dbms_output.put_line(v_anwen2_id);
-- INSERT INTO ty_join_anwen_rolesn (anwen,roles,client_id) VALUES (v_anwen2_id,v_roles_id,v_client1_id);
END IF;
-- delete from ty_join_roles_right where roles = v_old_roles;
END LOOP;
CLOSE c_users;
END LOOP;
CLOSE c_roles;
END;
/
With due respect to your efforts, I dare to say It is not a good coding practice to open up these many cursors.
You can achieve this by joining all your tables in one SQL. Try to consolidate as much logic as possible in one or two SQLs.
Sample SQLs for your reference -
Select a.abteil, a.client_id, c.anwen
From ty_join_anwen_abteil a, ty_sd_person b, ty_join_anwen_abteil c
Where a.anwen = b.id
And b.email = 'jens#gmail.com'
And c.abteil. = a.abteil;
Select c.anwen
From ty_join_rolesn_righte a, ty_sd_berightigung b, ty_join_anwen_rolesn c
Where b.description ='failure_note'
And a.right = b.id
And c.roles = a.id

Oracle Update with the Case When Exists clause

I need a trigger to update a table DIRECTORY_NUMBER when one value of DN_NUM column matches with MSISDN column value of a different table (RNPH_REQUETS_DETAILS) under a different schema(NKADM). The trigger will run every time there's a new entry in the DIRECTORY_NUMBER table. Based upon several conditions, the values of the DN_STATUS column and a few other columns need to be updated. The updated value of the DN_STATUS column will be 'r' if the conditions are met, and 'w' if the conditions are not met. Active portion of my code is given below:
UPDATE d
SET d.DN_STATUS = CASE WHEN EXISTS (SELECT 1 from NKADM.RNPH_REQUESTS_DETAILS n where n.MSISDN = d.DN_NUM AND n.PROCESS_STATE_ID = 4 AND n.ACTION='IN' AND n.FAILED_STATUS IS NULL AND TRUNC(n.MODIFICATION_DATE) = TRUNC(SYSDATE))
THEN 'r'
ELSE 'w'
END,
d.DN_MODDATE = SYSDATE,
d.BUSINESS_UNIT_ID = 2,
d.HLCODE = 5
WHERE d.DN_ID =: NEW.DN_ID
AND d.PLCODE = 1004
AND d.DN_STATUS = 'f'
FROM DIRECTORY_NUMBER d;
I am getting the following error:
Error(48,1): PL/SQL: SQL Statement ignored
Error(60,3): PL/SQL: ORA-00933: SQL command not properly ended
The errors get resolved only if I remove the references. But that gives a different result than intended. When the code is as follows:
UPDATE DIRECTORY_NUMBER
SET DN_STATUS = CASE WHEN EXISTS (SELECT 1 from NKADM.RNPH_REQUESTS_DETAILS where MSISDN = DN_NUM AND PROCESS_STATE_ID = 4
AND ACTION='IN' AND FAILED_STATUS IS NULL AND TRUNC(MODIFICATION_DATE) = TRUNC(SYSDATE))
THEN 'r'
ELSE 'w'
END,
DN_MODDATE =SYSDATE,
BUSINESS_UNIT_ID=2,
HLCODE =5
WHERE DN_ID =:NEW.DN_ID
AND PLCODE =1004
AND DN_STATUS ='f';
COMMIT;
Even when the CASE WHEN EXISTS condition is true (returns result when run independently), the value of DN_STATUS gets updated to 'w'.
Update: I tried with the following code:
UPDATE DIRECTORY_NUMBER
SET DN_STATUS = 'r',
DN_MODDATE =SYSDATE,
BUSINESS_UNIT_ID=2,
HLCODE =5
WHERE DN_ID =:NEW.DN_ID
AND PLCODE =1004
AND DN_STATUS ='f';
AND DN_NUM in (select MSISDN from NKADM.RNPH_PROCESS_DETAILS where PROCESS_STATE_ID = 4);
This isn't working either. If I remove the last condition, the resultant row has DN_STATUS value of 'f', and the MSISDN is in NKADM.RNPH_PROCESS_DETAILS table with PROCESS_STATE_ID = 4. I don't understand why it's not working.
What am I doing wrong?
In BEFORE update/insert trigger for EACH ROW you can modify data of record which is currently processed. You don't need to call an extra UPDATE to change the data.
In other words you can do something like this
IF :NEW.PLCODE = 1004 AND :NEW.DN_STATUS = 'f' THEN
:NEW.DN_MODDATE := SYSDATE;
:NEW.BUSINESS_UNIT_ID := 2;
:NEW.HLCODE := 5;
-- this query you can wrap in a function and call this function
SELECT COUNT(1)
INTO lv_count
FROM NKADM.RNPH_REQUESTS_DETAILS n
WHERE n.MSISDN = :NEW.DN_NUM
AND n.PROCESS_STATE_ID = 4
AND n.ACTION = 'IN'
AND n.FAILED_STATUS IS NULL
AND TRUNC(n.MODIFICATION_DATE) = TRUNC(SYSDATE);
IF lv_count > 0 THEN
:NEW.DN_STATUS := 'r';
ELSE
:NEW.DN_STATUS := 'w';
END IF;
END IF;

How to return multiple values through function

I have created the below function that will return workspace details which the loggedin user has access to.
But this function is returning only the first record from the select list.
I need all the records to be displayed as output.
Please modify it and let me know.
CREATE OR REPLACE FUNCTION "F_WORKSPACE_LOGIN_USERS" (
p_email VARCHAR2
) RETURN VARCHAR2 IS
l_error VARCHAR2(1000);
l_workspace VARCHAR2(1000);
l_teams VARCHAR2(1000);
l_team VARCHAR2(1000);
BEGIN
FOR i IN ( SELECT a.name workspace,
a.team_id id
FROM slackdatawarehouse.teams a,
( SELECT TRIM(workspaces) workspaces
FROM alluser_workspaces_fact
WHERE lower(email) = lower(p_email)
) b
WHERE a.team_id IN ( SELECT c.team_id
FROM slackdatawarehouse.team_tokens c
)
OR instr(', '
|| lower(b.workspaces),', '
|| lower(a.name) ) > 0
ORDER BY 1 ) LOOP
l_teams := l_team
|| ','
|| i.id;
l_teams := ltrim(rtrim(l_teams,','),',');
RETURN l_teams;
END LOOP;
END;
Current output is :
T6HPQ5LF7,T6XBXVAA1,T905JLZ62,T7CN08JPQ,T9MV4732M,T5PGS72NA,T5A4YHMUH,TAAFTFS0P,T69BE9T2A,T85D2D8MT,T858U7SF4,T9D16DF5X,T9DHDV61G,T9D17RDT3,T5Y03HDQ8,T5F5QPRK7
Required output is :
T6HPQ5LF7
T6XBXVAA1
T905JLZ62
i need output like above as one by one
I don't know what that code really does (can't test it), but this might be the culprit:
...
RETURN l_teams;
END LOOP;
As soon as code reaches the RETURN statement, it exits the loop and ... well, returns what's currently in L_TEAMS variable. Therefore, move RETURN out of the loop:
...
END LOOP;
RETURN l_teams;
If it still doesn't work as expected (which might be the case), have a look at pipelined functions (for example, on Oracle-base site) as they are designed to return values you seem to be looking for.
A simple example:
SQL> create or replace type t_dp_row as object
2 (deptno number,
3 dname varchar2(20));
4 /
Type created.
SQL> create or replace type t_dp_tab is table of t_dp_row;
2 /
Type created.
SQL> create or replace function f_depts
2 return t_dp_tab pipelined
3 is
4 begin
5 for cur_r in (select deptno, dname from dept)
6 loop
7 pipe row(t_dp_row(cur_r.deptno, cur_r.dname));
8 end loop;
9 return;
10 end;
11 /
Function created.
SQL> select * from table(f_depts);
DEPTNO DNAME
---------- --------------------
10 ACCOUNTING
20 RESEARCH
30 SALES
40 OPERATIONS
SQL>

how can i left join and add where condition its doesn't read the where it brings all the ridedriver object?

How can I LEFT and add WHERE condition?
It doesn't read the where it brings all the ridedriver object?
SELECT p
FROM RideDriverEmployeeBundle:Ridedriver p
LEFT JOIN Chaya3niUserBundle:Bookings b WITH b.idridedriver = p.id
WHERE p.frequency IS NOT NULL
AND p.nbrplaces > 0
AND b.iduser != 2
OR b.iduser IS NULL
If I understand your question correctly it is probably an issue with your WHERE clause. Try using parantheses with the OR portion of the clause.
SELECT p
FROM RideDriverEmployeeBundle:Ridedriver p
LEFT JOIN Chaya3niUserBundle:Bookings b WITH b.idridedriver = p.id
WHERE p.frequency IS NOT NULL
AND p.nbrplaces > 0
AND (b.iduser != 2 OR b.iduser IS NULL)
To answer the question you asked in the comments..
i want to select all the driverrides except the one that the user already booked in
Sounds like you just do not want the other conditions you have in your WHERE clause.
SELECT p
FROM RideDriverEmployeeBundle:Ridedriver p
LEFT JOIN Chaya3niUserBundle:Bookings b WITH b.idridedriver = p.id
WHERE b.iduser IS NULL
By doing a LEFT JOIN and then only taking records that did not exist in the joined table with WHERE .. IS NULL, this is doing a LEFT OUTER JOIN.
In your case Ridedriver is Table A and Bookings is Table B, so this will return records in Ridedriver that do not join to anything in Bookings.