combine calculation into one sql or pl/sql oracle - sql

I need to write a single sql or pl/sql to accomplish the following:
select value1 from table1;
-----------------------------
99
select value2 from table2;
------------------------------
120
I need to find out 99 is what percentage of 120?
Based on the formula P = X1/X2 * 100 the result will be 82.5%
Thanks for your help.

It'll work if both tables have a single row (that's what your examples suggest). I used CTE instead of your "real" tables, so query you might be interested in begins at line #7.
SQL> with
2 table1 (value1) as
3 (select 99 from dual),
4 table2 (value2) as
5 (select 120 from dual)
6 --
7 select a.value1 / b.value2 * 100 as result
8 from table1 a cross join table2 b;
RESULT
----------
82,5
SQL>
As you stated that PL/SQL could be an option, consider creating a function:
SQL> create or replace function f_pct(par_1 in number, par_2 in number)
2 return number
3 is
4 begin
5 return par_1 / par_2 * 100;
6 exception
7 when others then
8 -- in case of e.g. PAR_2 = 0
9 return null;
10 end;
11 /
Function created.
Let's test it:
SQL> select ename, sal, comm, f_pct(comm, sal) result
2 from emp;
ENAME SAL COMM RESULT
---------- ---------- ---------- ----------
SMITH 800
ALLEN 1600 300 18,75
WARD 1250 500 40
JONES 2975
MARTIN 1250 1400 112
BLAKE 2850
<snip>

Related

How to use CASE statement in FROM Clause to decide which table to be used for fetching data in Oracle?

I have two tables , HISTORY_DATA and CURRENT_DATA , I have an input parameter REPORT_DATE ,as per following Condition , I have to fetch data :
IF(REPORT_DATE<=TRUNC(SYSDATE-40))THEN
SCHD_TABLE:='HISTORY_DATA' ;
ELSE
SCHD_TABLE:='CURRENT_DATA';
END IF ;
I tried using dynamic SQL , but it is giving error as invalid table name .
SELECT * FROM ''||SCHD_TABLE||'' ;
Used CASE STATEMENT but it also gave Syntax error :
SELECT * FROM (CASE WHEN REPORT_DATE<=TRUNC(SYSDATE-40) THEN HISTORY_DATA
ELSE CURRENT_DATA
END)
Please guide how to resolve this .
Assuming that history_data and current_data have equivalent columns, you can use UNION ALL and two queries. Each query SELECTs everything from a table if and only if the condition depending on you variable in the WHERE clause is true. Since the conditions in the WHERE clauses are negations of each other, one query will return an empty set when the other returns something. So you'll only get the result from the table you want according to your condition.
SELECT *
FROM history_data
WHERE report_date <= trunc(sysdate - 40)
UNION ALL
SELECT *
FROM current_data
WHERE report_date > trunc(sysdate - 40);
And by the way, it's a CASE expression not a statement you have there.
One option is to use a function that returns refcursor. Here's an example.
First, sample tables:
SQL> create table history_data as
2 select deptno, ename, job from emp where deptno = 10;
Table created.
SQL> create table current_data as
2 select deptno, ename, job from emp where deptno = 20;
Table created.
Function returns data depending on par_report_date:
SQL> create or replace function f_test (par_report_date in date)
2 return sys_refcursor
3 is
4 rc sys_refcursor;
5 begin
6 if par_report_date <= trunc(sysdate) - 40 then
7 open rc for select * from history_data;
8 else
9 open rc for select * from current_data;
10 end if;
11 return rc;
12 end;
13 /
Function created.
Testing (history data):
SQL> select f_test(date '2021-02-15') history_data from dual;
HISTORY_DATA
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO ENAME JOB
---------- ---------- ---------
10 CLARK MANAGER
10 KING PRESIDENT
10 MILLER CLERK
Testing (current data):
SQL> select f_test(sysdate) current_data from dual;
CURRENT_DATA
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO ENAME JOB
---------- ---------- ---------
20 SMITH CLERK
20 JONES MANAGER
20 SCOTT ANALYST
20 ADAMS CLERK
20 FORD ANALYST
SQL>
Based on your comment (about dynamic SQL): I'm not sure what benefit you expect from it. Yes, you can "concatenate" appropriate table name to the select statement, but the outcome is just the same. For example:
SQL> create or replace function f_test (par_report_date in date)
2 return sys_refcursor
3 is
4 l_str varchar2(200);
5 rc sys_refcursor;
6 begin
7 l_str := 'select * from ' ||
8 case when par_report_date <= trunc(sysdate) - 40 then 'history_data'
9 else 'current_data'
10 end;
11 open rc for l_str;
12 return rc;
13 end;
14 /
Function created.
SQL> select f_test(date '2021-02-15') history_data from dual;
HISTORY_DATA
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO ENAME JOB
---------- ---------- ---------
10 CLARK MANAGER
10 KING PRESIDENT
10 MILLER CLERK
SQL>

Writing a PLSQL select in array

I'm using a DB 'A' to output a list of numbers :
123455
123456
123457
And I'm looking to build a dynamic statement to look into a DB 'B' with those results as a filter
a. Build an array with the values from DB 'A'
SELECT * FROM my_table
WHERE number in &array;
How can I achieve this ?
The DB 'B' is an Oracle DB.
Thanks
Hm. Looks like your "background" is not Oracle, because there are no "DB"s there. I mean, there are, but not in a context you're using them. If "DB" stands for a "Database", to me it looks as if you're actually talking about tables here.
Also, I don't understand what
The DB 'B' is in PLSQL means.
If "database" is a table, how is it in PL/SQL?
Anyway, to get you started: I'm fetching some data from Scott's EMP and DEPT tables. For collections, I'm using Oracle's built-in types.
These are employees in departments 10 and 20:
SQL> select deptno, ename
2 from emp
3 where deptno in (10, 20)
4 order by deptno, ename;
DEPTNO ENAME
---------- ----------
10 CLARK
10 KING
10 MILLER
20 ADAMS
20 FORD
20 JONES
20 SCOTT
20 SMITH
8 rows selected.
PL/SQL procedure which does something with them (the way I understood the question):
SQL> declare
2 l_a sys.odcinumberlist;
3 l_b sys.odcivarchar2list;
4 begin
5 select deptno
6 bulk collect into l_a
7 from dept
8 where deptno in (10, 20);
9
10 select ename
11 bulk collect into l_b
12 from emp
13 where deptno in (select * from table(l_a))
14 order by ename;
15
16 for i in l_b.first .. l_b.last loop
17 dbms_output.put_line(l_b(i));
18 end loop;
19 end;
20 /
ADAMS
CLARK
FORD
JONES
KING
MILLER
SCOTT
SMITH
PL/SQL procedure successfully completed.
SQL>
Lines #1 - 3 - declaration section
lines #5 - 8 - inserting values (departments) into l_a collection
Lines #10 - 14 - inserting values (employees) into l_b collection, based on values stored in l_a
Lines #16 - 18 - displaying contents of l_b
See if it helps.
[EDIT] After seeing your comment: as far as I can tell, you can't do what you wanted, not as simple as you'd want it to. This is how it works - you enter a comma-separated values as a parameter (that's your "array"), split it into rows and use the result as a subquery:
SQL> SELECT *
2 FROM dept
3 WHERE deptno IN ( SELECT REGEXP_SUBSTR ( '&&par_depts',
4 '[^,]+',
5 1,
6 LEVEL)
7 FROM DUAL
8 CONNECT BY LEVEL <= REGEXP_COUNT ( '&&par_depts', ',') + 1);
Enter value for par_depts: 10,20
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
SQL>
This is a SQL*Plus example; you'll probably have to substitute '&&par_depts' with :par_depts (depending on a tool you use).
Use the MEMBER operator.
First create a collection type in SQL:
CREATE TYPE int_list IS TABLE OF INT;
Then just use it in an SQL statement:
SELECT *
FROM my_table
WHERE value MEMBER OF int_list(123455, 123456, 123457);
Which, for the sample data:
CREATE TABLE my_table ( id, value ) AS
SELECT LEVEL, 123453 + LEVEL FROM DUAL CONNECT BY LEVEL <= 5;
Outputs:
ID
VALUE
2
123455
3
123456
4
123457
If you want it in PL/SQL then:
DECLARE
items int_list := int_list(123455, 123456, 123457);
BEGIN
FOR row IN (
SELECT *
FROM my_table
WHERE value MEMBER OF items
)
LOOP
DBMS_OUTPUT.PUT_LINE( row.id || ', ' || row.value );
END LOOP;
END;
/
Which, for the same data, outputs:
2, 123455
3, 123456
4, 123457
db<>fiddle here
However, if you just want to connect two databases then setup a database link.

Oracle - best way to return from a procedure an OUT SYS_REFCURSOR with all the rows and columns deleted from a table

I want to do something like this:
create or replace procedure get_deleted_rows(result_set OUT SYS_REFCURSOR) is
begin
delete from table1
where start_date >= sysdate - 30 and start_date <= sysdate
returning * bulk collect into result_set;
end;
I saw that creating a type table of... and using bulk collect into a declared variable of this type was a way to go. But, this table I'm deleting has several columns and doing this would generate a lot of complexity for me to place it in production.
Is there a more simple way to return all the rows from a delete from a procedure?
... and doing this would generate a lot of complexity for me
I'm afraid that you'll just have to accept it.
Here's an example of how to do it.
Sample table; I'll delete rows for employees whose salaries are higher than 2000.
SQL> select * from test order by sal;
ENAME SAL STARS
---------- ---------- ----------
SMITH 800
JAMES 950
ADAMS 1100 *
WARD 1250 *
MARTIN 1250 *
TURNER 1500 *
ALLEN 1600 *
BLAKE 2850 ** --> delete Blake, Jones and Scott
JONES 2975 **
SCOTT 3000 ***
10 rows selected.
Let's start:
SQL> create or replace type t_row is object
2 (ename varchar2(10), sal number, stars varchar2(10));
3 /
Type created.
SQL> create or replace type t_tab as table of t_row;
2 /
Type created.
Procedure:
SQL> create or replace procedure p_test (par_rc out sys_refcursor)
2 is
3 l_tab t_tab;
4 begin
5 delete from test
6 where sal > 2000
7 returning t_row(ename, sal, stars) bulk collect into l_tab;
8
9 open par_Rc for select * from table (l_tab);
10 end;
11 /
Procedure created.
Testing:
SQL> var l_rc refcursor
SQL>
SQL> exec p_test(:l_rc);
PL/SQL procedure successfully completed.
SQL> print l_rc
ENAME SAL STARS
---------- ---------- ----------
JONES 2975 ** --> Deleted, as expected
BLAKE 2850 **
SCOTT 3000 ***
What's left?
SQL> select * from test order by sal;
ENAME SAL STARS
---------- ---------- ----------
SMITH 800
JAMES 950
ADAMS 1100 *
WARD 1250 *
MARTIN 1250 *
TURNER 1500 *
ALLEN 1600 *
7 rows selected.
SQL>

SQL Oracle - Replace function in Where Clause

I want to use the REPLACE function in the Where clause to replace a character that I put into a input box. For example if I input R123;R321 I want it to change my input to R123','R321.
Example of my query:
Select
order_no
From
order
Where
order_no in ('&Order_No')
I input the data with this box:
Any help would be appreciated, or if there is some other way to do it without the REPLACE function.
That won't work like this, I'm afraid. You'll have to split input value into rows, e.g.
SQL> select deptno, ename
2 from emp
3 where deptno in (select regexp_substr('&&deptnos', '[^;]+', 1, level)
4 from dual
5 connect by level <= regexp_count('&&deptnos', ';') + 1
6 )
7 order by deptno, ename;
Enter value for deptnos: 10;20
DEPTNO ENAME
---------- ----------
10 CLARK
10 KING
10 MILLER
20 ADAMS
20 FORD
20 JONES
20 SCOTT
20 SMITH
8 rows selected.
SQL>

Is there a way to do a select statement and union 2 column, replacing any NULL values in one with information from the other

I'm using Oracle. I have two tables, one which stores Customer ID, Name, billing address; I have another table which stores Store ID, name and address. I need to SELECT (and use a UNION, this is part of an assignment) the CustomerID and their billing address, if their billing address is NULL it needs to replace it the NULL value with the address of the shop the customer is associated with.
The closest I've gotten to what I want is:
SELECT s.ordid, C.billingaddress FROM ShopOrder S
INNER JOIN Customer C
ON C.custid = S.custid
WHERE billingaddress = NULL
UNION
SELECT S.ordid, S.deliveryaddress FROM ShopOrder S
But this just displays the delivery addresses. I have a feeling I may need an Outerjoin or brackets but I'm not sure. Any ideas?
I have prepared a similar test case for you :
I have taken two tables, emp and emp1. I want to select the "comm" field from emp, but whenever it is NULL, I will take the value from emp1 which is NOT NULL. This is exactly the rule you mentioned.
SQL> DROP TABLE EMP1 PURGE
2 /
Table dropped.
SQL> CREATE TABLE EMP1 AS SELECT * FROM EMP
2 /
Table created.
SQL> UPDATE EMP1 SET COMM = 9999
2 /
14 rows updated.
SQL> COMMIT
2 /
Commit complete.
SQL> SELECT e.ename ,
2 CASE
3 WHEN e.comm IS NULL
4 THEN e1.comm
5 ELSE e.comm
6 END comm
7 FROM emp e,
8 EMP1 E1
9 WHERE E.EMPNO=E1.EMPNO
10 /
ENAME COMM
---------- ----------
SMITH 9999
ALLEN 300
WARD 500
JONES 9999
MARTIN 1400
BLAKE 9999
CLARK 9999
SCOTT 9999
KING 9999
TURNER 0
ADAMS 9999
JAMES 9999
FORD 9999
MILLER 9999
14 rows selected.
SQL>