calling column name from array oracle sql - sql

my table looks like:
Name Salary Salary_1 Salary_2
AAA 100 70 80
BBB 120 100 110
CCC 20 25 30
This what I want to do is to save sum for each salaries (so for salary, salary_1 and salary_2). I've created the pl/sql block:
declare
type col is table of varchar2(8);
type suma is table of number;
v_col col:=col('SALARY','SALARY_1','SALARY_2');
v_suma suma:=suma();
begin
for i in 1..3 loop
v_col.extend();
v_suma.extend();
select sum(v_col(i))
into v_suma(i) from employees;
dbms_output.put_line('value : ' ||v_suma(i));
end loop;
end;
In dbms_output I get:
value :
value :
value :
I think that I cannot call column names like that from the varray. Am I correct?
Any ideas how to resolve this problem?
Many thanks for your help
Kamil

You are right; you cannot sum the columns the way you are trying it. sum(v_col(i)) simply multiplies the content of v_col(i) (which is null) with the number of records in the table.
However, you don't need a loop to get the three sums:
declare
type suma is table of number;
v_suma suma := suma();
begin
v_suma.extend(3);
select sum(salary), sum(salary_1), sum(salary_2)
into v_suma(1), v_suma(2), v_suma(3)
from employees;
for i in 1 .. 3 loop
dbms_output.put_line('value : ' || v_suma(i));
end loop;
end;

Related

IF condition containing IN operator inside ORACLE Trigger is not working

I have a trigger that uses IF condition with IN operator and two variables v_audit_user and v_evdnse_user inside IN. Both variables are containing comma separated ID values. The trigger gets compiled successfully with no errors. I am not understanding why the IF condition with IN is not working. When I select the function that assigns value to the variables independently, I do see the comma separated values, so nothing is wrong with function (see screenshot).
create or replace TRIGGER TRG_CHK_HRCQA_CASE_ACTIONS
AFTER INSERT ON KDD_CASE_ACTIONS
FOR EACH ROW
DECLARE
user_audit kdd_review_owner.OWNER_ID%TYPE; /* The user that is displayed in audit */
user_evdnse kdd_review_owner.OWNER_ID%TYPE; /* The user that took action in evidence tab */
v_audit_user NUMBER; /* The HRCO/QA user from audit tab */
v_evdnse_user NUMBER; /* The HRCO/QA user from evidence tab */
LV_ERRORCODE VARCHAR2(1000);
BEGIN
/* pass the username into the variables */
SELECT kro.OWNER_ID into user_audit from kdd_review_owner kro where kro.OWNER_SEQ_ID = :NEW.ACTION_BY_ID;
SELECT kro.OWNER_ID into user_evdnse from kdd_review_owner kro where kro.OWNER_SEQ_ID = :NEW.ACTION_BY_ID;
/* fetch the comma separated IDs */
v_audit_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID,user_audit,'AUDIT');
v_evdnse_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID,user_evdnse,'EVDNSE');
-- select ENTITY_ID into v_evdnse_user from table(f_get_arg_table(F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID,user_evdnse,'EVDNSE')));
/* If the action taken is by QA or HRCO role */
IF (:NEW.ACTION_SEQ_ID in (v_audit_user,v_evdnse_user))
THEN
/* then insert record in the SC_HRCQA_CASE_ACTIONS table with IS_HRCO_QA flag as Y */
Insert into SC_HRCQA_CASE_ACTIONS (ACTION_SEQ_ID,ACTION_BY_ID,ACTION_TS,STATUS_CD,CASE_INTRL_ID,ACTION_ID,NEW_CASE_OWNR_ASSGN_ID,CASE_DUE_TS,PREV_CASE_OWNR_ASSGN_ID,IS_HRCO_QA)
values (:NEW.ACTION_SEQ_ID, :NEW.ACTION_BY_ID, :NEW.ACTION_TS, :NEW.STATUS_CD, :NEW.CASE_INTRL_ID, :NEW.ACTION_ID, :NEW.NEW_CASE_OWNR_ASSGN_ID, :NEW.CASE_DUE_TS, :NEW.PREV_CASE_OWNR_ASSGN_ID,'Y');
-- ELSE
--
-- /* else the logged in user is NOT HRCO/QA hence insert record in the SC_HRCQA_CASE_ACTIONS table with IS_HRCO_QA flag as N */
--
-- Insert into SC_HRCQA_CASE_ACTIONS (ACTION_SEQ_ID,ACTION_BY_ID,ACTION_TS,STATUS_CD,CASE_INTRL_ID,ACTION_ID,NEW_CASE_OWNR_ASSGN_ID,CASE_DUE_TS,PREV_CASE_OWNR_ASSGN_ID,IS_HRCO_QA)
-- values (:NEW.ACTION_SEQ_ID, :NEW.ACTION_BY_ID, :NEW.ACTION_TS, :NEW.STATUS_CD, :NEW.CASE_INTRL_ID, :NEW.ACTION_ID, :NEW.NEW_CASE_OWNR_ASSGN_ID, :NEW.CASE_DUE_TS, :NEW.PREV_CASE_OWNR_ASSGN_ID,'N');
END IF;
EXCEPTION
WHEN OTHERS THEN LV_ERRORCODE := SQLCODE;
INSERT INTO KDD_LOGS_MSGS (LOG_DT, LOG_INFO_TX, REMARK_TX)
VALUES (SYSDATE,'ErrorCode - ' || LV_ERRORCODE,'TRG_CHK_HRCQA_CASE_ACTIONS');
END;
You cannot pass a comma-delimited string stored in a single variable to an IN condition and expect it to be parsed as multiple values as it is not.
If you want to use a single variable containing a delimited list then you will need to use string functions to find a sub-string match:
create or replace TRIGGER TRG_CHK_HRCQA_CASE_ACTIONS
AFTER INSERT ON KDD_CASE_ACTIONS
FOR EACH ROW
DECLARE
v_owner_id kdd_review_owner.OWNER_ID%TYPE;
v_audit_user VARCHAR2(1000);
v_evdnse_user VARCHAR2(1000);
LV_ERRORCODE VARCHAR2(1000);
BEGIN
SELECT OWNER_ID
into v_owner_id -- You only need one variable here
from kdd_review_owner
where OWNER_SEQ_ID = :NEW.ACTION_BY_ID;
v_audit_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID, v_owner_id, 'AUDIT');
v_evdnse_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID, v_owner_id, 'EVDNSE');
IF ','||v_audit_user||','||v_evdnse_user||',' LIKE '%,'||:NEW.ACTION_SEQ_ID||',%'
THEN
Insert into SC_HRCQA_CASE_ACTIONS (
ACTION_SEQ_ID, ACTION_BY_ID, ACTION_TS, STATUS_CD, CASE_INTRL_ID,
ACTION_ID, NEW_CASE_OWNR_ASSGN_ID, CASE_DUE_TS, PREV_CASE_OWNR_ASSGN_ID, IS_HRCO_QA
) values (
:NEW.ACTION_SEQ_ID, :NEW.ACTION_BY_ID, :NEW.ACTION_TS, :NEW.STATUS_CD, :NEW.CASE_INTRL_ID,
:NEW.ACTION_ID, :NEW.NEW_CASE_OWNR_ASSGN_ID, :NEW.CASE_DUE_TS, :NEW.PREV_CASE_OWNR_ASSGN_ID,'Y'
);
END IF;
EXCEPTION
WHEN OTHERS THEN LV_ERRORCODE := SQLCODE;
INSERT INTO KDD_LOGS_MSGS (LOG_DT, LOG_INFO_TX, REMARK_TX)
VALUES (SYSDATE,'ErrorCode - ' || LV_ERRORCODE,'TRG_CHK_HRCQA_CASE_ACTIONS');
END;
/
I don't have your tables so I'll try to illustrate it using my own code.
This is what you're doing now: to us (humans), it is obvious that L_NEW_ID (its value is 10) is contained in L_VAR1 (its value is '10,20'). However, Oracle doesn't recognize that and returns "Not OK":
SQL> declare
2 -- does L_NEW_ID exist in L_VAR1 and L_VAR2 (using your code)?
3 l_new_id number := 10;
4 --
5 l_var1 varchar2(20) := '10,20';
6 l_var2 varchar2(20) := '30,40';
7 begin
8 if l_new_id in (l_var1, l_var2) then
9 dbms_output.put_line('OK');
10 else
11 dbms_output.put_line('Not OK');
12 end if;
13 end;
14 /
Not OK
PL/SQL procedure successfully completed.
SQL>
So, what to do? One option is to split variables to rows and then check whether search value (10, right?) exists in such a list of values (rows). The result is - as you can see - "OK":
SQL> declare
2 l_new_id number := 10;
3 --
4 l_var1 varchar2(20) := '10,20';
5 l_var2 varchar2(20) := '30,40';
6 l_cnt number;
7 begin
8 -- count how many times L_NEW_ID exists in L_VAR1 nad L_VAR2
9 select count(*)
10 into l_cnt
11 from (-- split L_VAR1 into rows
12 select regexp_substr(l_var1, '[^,]+', 1, level) val
13 from dual
14 connect by level <= regexp_count(l_var1, ',') + 1
15 union all
16 -- split L_VAR2 into rows
17 select regexp_substr(l_var2, '[^,]+', 1, level)
18 from dual
19 connect by level <= regexp_count(l_var2, ',') + 1
20 ) x
21 where x.val = l_new_id;
22
23 if l_cnt > 0 then
24 dbms_output.put_line('OK');
25 else
26 dbms_output.put_line('Not OK');
27 end if;
28 end;
29 /
OK
PL/SQL procedure successfully completed.
SQL>
Just to verify it, let's change L_NEW_ID value to e.g. 99 and see what happens; as expected, "Not OK" as 99 isn't contained in 10, 20 nor 30, 40.
SQL> l2
2* l_new_id number := 10;
SQL> c/10/99
2* l_new_id number := 99;
SQL> /
Not OK
PL/SQL procedure successfully completed.
SQL>

check if two values are present in a table with plsql in oracle sql

I'm trying to create a procedure, that checks if two values are present in a table.
The logic is as follows: Create a function called get_authority. This function takes two parameters (found in the account_owner table): cust_id and acc_id, and returns 1 (one), if the customer has the right to make withdrawals from the account, or 0 (zero), if the customer doesn't have any authority to the account. I'm writing plsql and using oracle live sql. I can't figure out how to handle the scenario where a customer has two accounts!
account_owner is seen here:
create or replace function get_authority(
p_cust_id in account_owner.cust_id%type,
p_acc_id in account_owner.acc_id%type
)
return varchar2
as
v_return number(1);
v_acc_id account_owner.acc_id%type;
v_cust_id account_owner.cust_id%type;
begin
for v_ in (select account_owner.cust_id,
account_owner.acc_id
from account_owner
where p_cust_id = cust_id)
LOOP
if p_cust_id = v_cust_id and p_acc_id = v_acc_id then
v_return := v_return + 1;
else
v_return := v_return + 0;
end if;
return v_return;
END LOOP;
end;
/
When I check for the cust_id I get the return 0 - but it should be 1??
select get_authority('650707-1111',123) from dual;
return:
GET_AUTHORITY('650707-1111',123)
0
What do I do wrong?
You got 0? How come; should be NULL.
v_return number(1);
so it is initially NULL. Later on, you're adding "something" to it, but - adding anything to NULL will be NULL:
SQL> select 25 + null as result from dual;
RESULT
----------
SQL>
Therefore, set its default value to 0 (zero):
v_return number(1) := 0;
Also, you declared two additional variables:
v_acc_id account_owner.acc_id%type;
v_cust_id account_owner.cust_id%type;
Then you compare them to values passed as parameters; as they are NULL, ELSE is executed.
Furthermore, there's a loop, but you don't do anything with it. If you meant that this:
for v_ in (select account_owner.cust_id,
(rewritten as for v_ in (select cust_id) evaluates to v_cust_id - it does not. Cursor variables are referred to as v_.cust_id (note the dot in between).
Also, if there's only one row per p_cust_id and p_acc_id, why do you use cursor FOR loop at all? To avoid no_data_found or too_many_rows? I wouldn't do that; yes, it fixes such "errors", but is confusing. You'd rather properly handle exceptions.
Here's what you might have done:
Sample data:
SQL> select * From account_owner;
ACCOW_ID CUST_ID ACC_ID
---------- ----------- ----------
1 650707-1111 123
2 560126-1148 123
3 650707-1111 5899
Function; if there are more rows per parameters' combination, max function will make sure that too_many_rows is avoided (as it bothers you). You don't really care what it returns - important is that select returns anything to prove that authority exists for that account.
SQL> create or replace function get_authority
2 (p_cust_id in account_owner.cust_id%type,
3 p_acc_id in account_owner.acc_id%type
4 )
5 return number
6 is
7 l_accow_id account_owner.accow_id%type;
8 begin
9 select max(o.accow_id)
10 into l_accow_id
11 from account_owner o
12 where o.cust_id = p_cust_id
13 and o.acc_id = p_acc_id;
14
15 return case when l_accow_id is not null then 1
16 else 0
17 end;
18 end;
19 /
Function created.
Testing:
SQL> select get_authority('650707-1111', 123) res_1,
2 get_authority('650707-1111', 5899) res_2
3 from dual;
RES_1 RES_2
---------- ----------
1 1
SQL>

Function returns max of whole table instead of someting particular - PLSQL

I got a PLSQL question related to my output.
Suppose i have a table like this:
Rates:
Company | Country | Level
AA | US | 5
BB | UK | 4
CC | FRANCE | 2
DD | FRANCE | 3
EE | US | 4
FF | UK | 5
I need to create a FUNCTION that gets as parameter the country and provides me it's maximal level.
this is what i wrote:
CREATE OR REPLACE
FUNCTION getMaxLevel(Country VARCHAR2)
RETURN NUMBER
IS
MaxLevel NUMBER;
BEGIN
SELECT max(R.Level) INTO MaxLevel
FROM Rates R
WHERE R.country=Country;
RETURN MaxLevel;
END getMaxLevel;
/
/*CALL TO CHECK*/
DECLARE
X NUMBER;
BEGIN
X:=getMaxLevel('FRANCE');
dbms_output.put_line(X);
END;
and my output is the maximal rate of whole Rates table which here for instance is 6, but i needed to get 3.
Will be thankful for finding my bug :)
Thanks!
You problem is the parameter name. I would recommend prefixing it with something like in:
CREATE OR REPLACE
FUNCTION getMaxLevel(
in_Country VARCHAR2
)
RETURN NUMBER IS
MaxLevel NUMBER;
BEGIN
SELECT max(R.Level) INTO MaxDegree
FROM Rates R
WHERE R.country = in_Country;
RETURN MaxLevel;
END getMaxLevel;
/
You think that Country in your expression:
R.country = Country
is referring to the parameter. But that is not how SQL scoping rules work. It is referring to R.country -- hence the non-sensical result.
You should give your parameter another name that the name of the column in the table, otherwise the condition in the WHERE clause is ambiguous.
In condition R.country = Country, the database thinks that the second Country refers to the (unqualified) column name, not to the parameter. This condition is always true (unless Country is null), and the query ends up returning the maximum level from the whole table.
CREATE OR REPLACE
FUNCTION getMaxLevel(pCountry VARCHAR2)
RETURN NUMBER
IS
MaxLevel NUMBER;
BEGIN
SELECT max(R.Level) INTO MaxLevel
FROM Rates R
WHERE R.country = pCountry;
RETURN MaxLevel;
END getMaxLevel;
/
/*CALL TO CHECK*/
DECLARE
X NUMBER;
BEGIN
X:=getMaxLevel('FRANCE');
dbms_output.put_line(X);
END;
Side note: there is a typo in the query, you meant SELECT .. INT0 MaxLevel instead of SELECT .. INT0 MaxDegree.
You forgot tо prefix the function parameter, this is the name of the function itself.
Rename the column level in the table (e.g. lvl), it is the key word.
Consider this working example:
create or replace function getMaxLevel (Country varchar2) return number is
begin
for r in (
select max (r.Lvl) MaxLevel
from Rates r
where R.country=getMaxLevel.Country
) loop return r.MaxLevel;
end loop;
raise program_error;
end getMaxLevel;
/
var x number
exec :x := getMaxLevel ('FRANCE');
X
-
3

Using pl/sql output in WHERE-statement

I have a little question. So I'm using the Levenshtein-score to search for a comparison of more than 85% between street names in two different tables. But when I use my Levenshtein-score calculation in my WHERE-statement, I get as output for example this:
street names in one table: BEAU SITE 1ÈRE AVENUE & BEAU SITE 2ÈME AVENUE
street names in the other table: BEAU SITE-1ÈRE AVENUE & BEAU SITE-2ÈRE AVENUE
output: linking between all, first one with first and second on, and second one with first and second.
So I have to use the largest score for all the score-calculations and is this like this:
DECLARE
L_SCORE NUMBER;
L_NEW_SCORE NUMBER;
L_BEST_MAP varchar2(255);
CURSOR C_TO_FIND IS
SELECT TT_NAME, L_MUNI, R_MUNI
FROM Y_TT_NOT_LINKED_STREETS ;
CURSOR C_TOMTOM_STREET (L_MUNI VARCHAR2) IS
SELECT STREET_NAME
FROM STREET_NAME SN
JOIN STREET STR ON STR.STREET_ID = SN.STR_STREET_ID
JOIN ADMINISTRATIVE_AREA_NAME AAN ON AAN.AAR_ADMIN_AREA_ID = STR.AAR_ADMIN_AREA_ID
WHERE AAN.ADMIN_AREA_NAME = L_MUNI
AND STR.STREET_ID NOT IN (SELECT ROMA_STREET_ID FROM Y_DS_STREETS_LINK);
BEGIN
FOR S IN C_TO_FIND LOOP
L_SCORE := 0;
L_NEW_SCORE := 0;
FOR R IN C_TOMTOM_STREET(S.L_MUNI) LOOP
L_SCORE := PCK$ADDRESSMATCH.GET_LEVENSHTEIN_SCORE(S.TT_NAME,R.STREET_NAME);
IF L_SCORE > L_NEW_SCORE THEN
L_NEW_SCORE := L_SCORE ;
L_BEST_MAP := R.STREET_NAME ||CHR(9)||TO_CHAR(L_NEW_SCORE);
END IF;
END LOOP;
IF L_NEW_SCORE > 85 THEN
DBMS_OUTPUT.PUT_LINE(S.L_MUNI||CHR(9)||S.TT_NAME||chr(9)||L_BEST_MAP);
END IF;
L_NEW_SCORE := 0;
END LOOP;
END;
Now is the question, how can I use the output in a WHERE-statement, so that I can link with only the largest Levenshtein-score and the problem above will not occur?
So that:
SELECT ...
FROM ...
WHERE (largest score from previous block code)
(or on another way, after a whole week doing SQL I can't see a solution for this)
Thx! =)

COUNT function for files?

Is it possible to use COUNT in some way that will give me the number of tuples that are in a .sql file? I tried using it in a query with the file name like this:
SELECT COUNT(*) FROM #q65b;
It tells me that the table is invalid, which I understand because it isn't a table, q65b is a file with a query saved in it. I'm trying to compare the number of rows in q65b to a view that I have created. Is this possible or do I just have to run the query and check the number of rows at the bottom?
Thanks
You can do this in SQL*Plus. For example:
Create the text file, containing the query (note: no semicolon!):
select * from dual
Save it in a file, e.g. myqueryfile.txt, to the folder accessible from your SQL*Plus session.
You can now call this from within another SQL query - but make sure the # as at the start of a line, e.g.:
SQL> select * from (
2 #myqueryfile.txt
3 );
D
-
X
I don't personally use this feature much, however.
Here is one approach. It's a function which reads a file in a directory, wraps the contents in a select count(*) from ( .... ) construct and executes the resultant statement.
1 create or replace function get_cnt
2 ( p_file in varchar2 )
3 return number
4 as
5 n pls_integer;
6 stmt varchar2(32767);
7 f_line varchar2(255);
8 fh utl_file.file_type;
9 begin
10 stmt := 'select count(*) from (';
11 fh := utl_file.fopen('SQL_SCRIPTS', p_file, 'R');
12 loop
13 utl_file.get_line(fh, f_line );
14 if f_line is null then exit;
15 elsif f_line = '/' then exit;
16 else stmt := stmt ||chr(10)||f_line;
17 end if;
18 end loop;
19 stmt := stmt || ')';
20 execute immediate stmt into n;
21 return n;
22* end get_cnt;
SQL>
Here is the contents of a sql file:
select * from emp
/
~
~
~
"scripts/q_emp.sql" 3L, 21C
And here is how the script runs:
SQL> select get_cnt ('q_emp.sql') from dual
2 /
GET_CNT('Q_EMP.SQL')
--------------------
14
SQL>
So it works. Obviously what I have posted is just a proof of concept. You will need to include lots of error handling for the UTL_FILE aspects - it's a package which can throw lots of exceptions - and probably some safety checking of the script that gets passed.