picking 1 column from 2 tables and comparing them - oracle

In my Oracle database there are two tables which are TEMP_HR and PAY_SLIP_APR_16. Both of them have a common column named EMP_ID. TEMP_HR has over 10,000 records and PAY_SLIP_APR_16 has around 6,000 records. I want to know how many EMP_ID of PAY_SLIP_APR_16 is matched with TEMP_HR. If any ID doesn't match then print it. And here is my simple approach but I think its a very bad approach. So any faster method?
DECLARE
INPUT_EMP_NO VARCHAR2(13 BYTE);
INPUT_EMP_ID VARCHAR2(13 BYTE);
ROW_COUNT_1 NUMBER(6,0);
ROW_COUNT_2 NUMBER(6,0);
MATCHED_ID NUMBER;
UNMATCHED_ID NUMBER;
BEGIN
ROW_COUNT_1:=0;
ROW_COUNT_2:=0;
MATCHED_ID:=0;
UNMATCHED_ID:=0;
SELECT COUNT(*) INTO ROW_COUNT_1 FROM PAY_SLIP_APR_16;
SELECT COUNT(*) INTO ROW_COUNT_2 FROM TEMP_HR;
FOR A IN 1..ROW_COUNT_1 LOOP
BEGIN
SELECT EMP_ID INTO INPUT_EMP_ID FROM (SELECT EMP_ID, ROWNUM AS RN FROM PAY_SLIP_APR_16) WHERE RN=A;
FOR B IN 1..ROW_COUNT_2 LOOP
SELECT EMP_NO INTO INPUT_EMP_NO FROM (SELECT EMP_NO, ROWNUM AS RON FROM TEMP_HR) WHERE RON=B;
IF(INPUT_EMP_ID=INPUT_EMP_NO)THEN
MATCHED_ID:=MATCHED_ID+1;
EXIT;
ELSE
CONTINUE;
END IF;
END LOOP;
UNMATCHED_ID:=UNMATCHED_ID+1;
DBMS_OUTPUT.PUT_LINE(INPUT_EMP_ID);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(INPUT_EMP_ID||' -> '||SQLERRM);
END;
END LOOP;
DBMS_OUTPUT.PUT_LINE('MATCHED -> '||MATCHED_ID);
DBMS_OUTPUT.PUT_LINE('UNMATCHED -> '||UNMATCHED_ID);
END;

Use an outer join filtering for missed joins:
select p.*
from PAY_SLIP_APR_16 p
left join TEMP_HR t on t.EMP_ID = p.EMP_ID
where t.EMP_ID is null
An index on TEMP_HR(EMP_ID) will make this query fly.

You should use SQL sets!
To check which EMP_ID isn't in TEMP_HR you may try this:
select EMP_ID FROM PAY_SLIP_APR_16
where EMP_ID not in (select EMP_NO from TEMP_HR);
Then the other way around:
select EMP_NO FROM TEMP_HR
where EMP_NO not in (select EMP_ID from PAY_SLIP_APR_16);

Related

PL/SQL: ORA-00947: not enough values

Im creating a procedure to display the n number of maximum and minimum salary for an employee. If i ll give 5 as input, the query will get me 5 maximum and minimum salary for an employee.
For the above scenario, I have created an object with two columns like below
create type vrec as object(
empno number,
sal number
);
/
Then i created nested table with the help of object type, so that i can use the nested table as out parameter to return all the rows at one short.
create type vrec_type is table of vrec;
/
After the data type creation, im creating a procedure like below
create or replace procedure pro_first_last(input in number,salary out vrec_type)
is
begin
select empno,sal BULK COLLECT INTO salary from (
select empno,sal from
(select empno,sal,rank() over(order by sal asc) min_sal from emp5 where sal is not null) where min_sal <= 5
union all
select empno,sal from
(select empno,sal,rank() over(order by sal desc) max_sal from emp5 where sal is not null) where max_sal <= 5);
for i in salary.first..salary.last
loop
dbms_output.put_line(salary(i).empno);
end loop;
end;
/
When i compiling the above procedure, im getting not enough values. I have also created the object with two columns and in select statement also im returning only two column. Could someone review and help me on this or provide some alternate solution.
You are directly adding empno, sal values into salary (vrec_type object, which can take the values of only object type vrec)
You need to create the object of vrec and then add it into salary as following:
create or replace procedure pro_first_last(input in number,salary out vrec_type)
is
begin
select vrec(empno,sal) -- change in this line
BULK COLLECT INTO salary from (
select empno,sal from
(select empno,sal,rank() over(order by sal asc) min_sal from emp5 where sal is not null) where min_sal <= 5
union all
select empno,sal from
(select empno,sal,rank() over(order by sal desc) max_sal from emp5 where sal is not null) where max_sal <= 5);
for i in salary.first..salary.last
loop
dbms_output.put_line(salary(i).empno);
end loop;
end;
Cheers!!

PL\SQL Hot do I convert select value to bit flag?

I would like to have pl-sql like the following:
SELECT ID FROM some-table WHERE
(SELECT MAX(some-expression) FROM another-table = 1) OR
(some-table.ID IN SELECT (SELECT ID FROM one-more-table))
This pseudo-query would select all IDs from some-table if maximum value of some-expression equals 1 (or filter IDs by one-more-table values otherwise)
How do I properly implement this in PL-SQL ?
Thank you in advance!
Please find the below pl-sql:
DECLARE
v_Column1 NUMBER(4);
v_deptno NUMBER(2);
BEGIN
SELECT MAX(A.DEPTNO)
INTO v_deptno
FROM (SELECT DEPTNO FROM DEPT) A;
SELECT A.EMPNO
INTO v_Column1
FROM EMP A
WHERE A.DEPTNO=v_deptno
OR
A.EMPNO IN ( SELECT EMPNO FROM EMP_2);
END;
/

oracle select from table if values not exists

I need to write oracle query (Just query)
to select values from table, and if not found select from another table.
any help to do this in pl/sql?
SELECT * FROM firstTable
UNION ALL
SELECT * FROM secondTable WHERE (SELECT count(*) FROM FIRST_TABLE ) = 0
You can enclose the SELECT statement within a block and add an exception handler to it.
So, if there are no rows selected from the first table, then you select from the second table. The structure would be something like below:
Begin
select <many columns> into <many variables or row type>
From Table1
where <conditions>;
EXCEPTION
WHEN NO_DATA_FOUND THEN
select <many columns> into <many variables or row type>
From Table2
Where <Conditions>;
End;
References:
Another related SO question
Exception Handlers
Documentation for the SELECT INTO statement
Here is an example of a PL/SQL function that will perform a test, and then execute a secondary query based upon the results of the test. You can adjust it to fit your needs:
set serveroutput on;
declare
row_count number;
column1 varchar(10);
column2 varchar(10);
column3 number;
begin
/*Perform your test*/
select count(target_column) into row_count
from my_table
where condition_column = 'x';
/*Run your secondary query based on the output of the first*/
if row_count > 0 then
select
col_x into column1,
col_y into column2,
col_z into column3
from my_other_table_A;
else
select
col_a into column1,
col_b into column2,
col_c into column3
from my_other_table_B;
end if;
/*Show the results*/
dbms_output.put_line('column1: ' || column1);
dbms_output.put_line('column2: ' || column2);
dbms_output.put_line('column3: ' || column3);
end;
/

How to pass cursor values into variable?

I am trying to read values from two column1, column2 from table1 using cursor. Then I want to pass these values to another cursor or select into statement
so my PL/Sql script will use the values of these two columns to get data from another table called table2
Is this possible? And what's the best and fastest way to do something like that?
Thanks :)
Yes, it's possible to pass cursor values into variables. Just use fetch <cursor_name> into <variable_list> to get one more row from a cursor. After that you can use the variables in where clause of some select into statement. E.g.,
declare
cursor c1 is select col1, col2 from table1;
l_col1 table1.col1%type;
l_col2 table1.col2%type;
l_col3 table2.col3%type;
begin
open c1;
loop
fetch c1 into l_col1, l_col2;
exit when c1%notfound;
select col3
into l_col3
from table2 t
where t.col1 = l_col1 --Assuming there is exactly one row in table2
and t.col2 = l_col2; --satisfying these conditions
end loop;
close c1;
end;
If you use an implicit cursor, then it's even simpler:
declare
l_col3 table2.col3%type;
begin
for i in (select col1, col2 from table1)
loop
select col3
into l_col3
from table2 t
where t.col1 = i.col1 --Assuming there is exactly one row in table2
and t.col2 = i.col2; --satisfying these conditions
end loop;
end;
In these examples, it's more efficient to use a subquery
begin
for i in (select t1.col1
, t1.col2
, (select t2.col3
from table2 t2
where t2.col1 = t1.col1 --Assuming there is atmost one such
and t2.col2 = t1.col2 --row in table2
) col3
from table1 t1)
loop
...
end loop;
end;

How to use list of selected values on the body of procedure

I need help with the structure of the procedure.
First of all, I should retrieve list of table names and owners.
At the body of the procedure I want to use this list for testing and comparisons.
I made some example that used cursor, I know it is not true.
Please advise how to implement this.
CREATE OR REPLACE PROCEDURE TEST_PROCEDURE (P_CODE IN NUMBER) IS
CURSOR C01 IS
(SELECT TABLE_NAME, OWNER
FROM TABLE1
UNION
SELECT TABLE_NAME, OWNER
FROM TABLE2);
BEGIN
SELECT MAX(SCORE)
INTO V_SCORE
FROM TABLE4 Q
WHERE EXISTS (SELECT 'Y'
FROM C01 T --- ?????????
WHERE Q.TABLE_NAME = T.TAB_NAME
AND Q.OWNER = T.OWNER);
END TEST_PROCEDURE;
The idea is good, but goes through some small misconceptions. Cursors are not intented to replace views or full resultsets, they are just allowing you to parse one by one (like a cursor) a give resultset.
You could use a common table expression (CTE) in this case:
CREATE OR REPLACE PROCEDURE TEST_PROCEDURE (P_CODE IN NUMBER) IS
BEGIN
WITH C01 AS (
SELECT
TABLE_NAME,
OWNER
FROM TABLE1
UNION
SELECT
TABLE_NAME,
OWNER
FROM TABLE2
)
SELECT MAX(SCORE) INTO V_SCORE
FROM TABLE4 Q
WHERE
EXISTS (
SELECT 'Y'
FROM C01 T
WHERE Q.TABLE_NAME = T.TAB_NAME
AND Q.OWNER = T.OWNER
);
END TEST_PROCEDURE;
If you need this CTE multiple times, then why not creating a view?
CREATE OR REPLACE VIEW C01 AS
SELECT
TABLE_NAME,
OWNER
FROM TABLE1
UNION
SELECT
TABLE_NAME,
OWNER
FROM TABLE2
;
CREATE OR REPLACE PROCEDURE TEST_PROCEDURE (P_CODE IN NUMBER) IS
BEGIN
SELECT MAX(SCORE) INTO V_SCORE
FROM TABLE4 Q
WHERE
EXISTS (
SELECT 'Y'
FROM C01 T
WHERE Q.TABLE_NAME = T.TAB_NAME
AND Q.OWNER = T.OWNER
);
END TEST_PROCEDURE;
Or even better, since you seem to just check that a set of values exist, create a deterministic function for this:
CREATE OR REPLACE FUNCTION EXISTS_IN_TABLES(I_OWNER IN VARCHAR2, I_TABLE IN VARCHAR2) RETURNS NUMBER DETERMINISTIC AS
BEGIN
SELECT 1
FROM (
SELECT TABLE_NAME, OWNER
FROM TABLE1
UNION
SELECT TABLE_NAME, OWNER
FROM TABLE2
) T
WHERE I_TABLE = T.TAB_NAME
AND I_OWNER = T.OWNER;
RETURN 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN RETURN 0;
WHEN OTHERS THEN RAISE;
END;
CREATE OR REPLACE PROCEDURE TEST_PROCEDURE (P_CODE IN NUMBER) IS
BEGIN
SELECT MAX(SCORE) INTO V_SCORE
FROM TABLE4 Q
WHERE
EXISTS_IN_TABLES(Q.OWNER, Q.TABLE_NAME) = 1
;
END TEST_PROCEDURE;
The deterministic option optimizes the performances, but then you have to be sure the content of TABLE1 and TABLE2 does not change during current session.

Resources