SQL statement that repeats itself until there are no more results - sql

I have the below existing statement. It finds parts that exist within other builds. The problem is, my user wants to go further until there are no more results.
For example.
We look up where PartA is used.
We find it is used inside of PartB and PartC.
Then we want to run the query again to find where PartB is used and PartC.
If PartB is not used anywhere else (No results found), we want to return PartB as part of the results.
But if PartC is used somewhere else, we want to keep going until we get no result for each part.
I am not sure if this is even possible so I thought I would ask here.
select ms.contract site,
ms.part_no,
crar1app.INVENTORY_PART_API.GET_DESCRIPTION(ms.contract, ms.part_no) part_desc,
ms.QTY_PER_ASSEMBLY,
ms.PRINT_UNIT uom,
-- ms.ENG_CHG_LEVEL,
crar1app.ENG_PART_REVISION_API.GET_PART_REV(ms.PART_NO, ms.ENG_CHG_LEVEL) rev,
ms.EFF_PHASE_IN_DATE,
ms.EFF_PHASE_OUT_DATE,
ms.BOM_TYPE,
ms.ALTERNATIVE_NO alt
from crar1app.MANUF_STRUCTURE ms
where ms.CONTRACT = nvl('&SITE','10')
and ms.COMPONENT_PART = '&PART_NO'
and ms.EFF_PHASE_IN_DATE <= to_date(nvl('&EFF_DATE',to_char(SYSDATE,'YYYY-MM-DD')),'YYYY-MM-DD')
and (ms.EFF_PHASE_OUT_DATE > to_date(nvl('&EFF_DATE',to_char(SYSDATE,'YYYY-MM-DD')),'YYYY-MM-DD')
or ms.EFF_PHASE_OUT_DATE is null)
and (ms.ALTERNATIVE_NO = 'ML'
or (select 1 from dual where crar1app.MANUF_STRUCT_ALTERNATE_API.GET_OBJSTATE(ms.CONTRACT,ms.PART_NO,ms.ENG_CHG_LEVEL,ms.BOM_TYPE,'ML') in ('Plannable','Buildable')) IS NULL)
I have tried a few things but nothing close to what I am looking for.

Lots of options are available to you in a CONNECT BY query which lets you loop through a hierarchy, eg
SQL> create table parts as
2 select empno part_num, mgr parent_part
3 from scott.emp;
Table created.
SQL>
SQL> select
2 level,
3 part_num,
4 parent_part,
5 sys_connect_by_path(part_num,'-') path,
6 connect_by_isleaf
7 from parts
8 connect by prior part_num = parent_part
9 start with parent_part is null;
LEVEL PART_NUM PARENT_PART PATH CONNECT_BY_ISLEAF
---------- ---------- ----------- ------------------------------ -----------------
1 7839 -7839 0
2 7566 7839 -7839-7566 0
3 7788 7566 -7839-7566-7788 0
4 7876 7788 -7839-7566-7788-7876 1
3 7902 7566 -7839-7566-7902 0
4 7369 7902 -7839-7566-7902-7369 1
2 7698 7839 -7839-7698 0
3 7499 7698 -7839-7698-7499 1
3 7521 7698 -7839-7698-7521 1
3 7654 7698 -7839-7698-7654 1
3 7844 7698 -7839-7698-7844 1
3 7900 7698 -7839-7698-7900 1
2 7782 7839 -7839-7782 0
3 7934 7782 -7839-7782-7934 1

Related

Get data starting from the middle of the table

I have a question. I want to get data from the middle of the table. But the problem is I can't start from the middle. I have read that rownum can't be used for this. But i dont know how to do this with another method. Anyone have any clue how to do this? Thank you.
Below is an example.
select count(1) --9857
from time_day_dm
where rownum between 5000 and 9857
The output would be 0.
What i expect would be 4857.
rownum won't help here (as you already know).
What you might do, is to use a couple of analytic functions (count, to find number of rows in the table; row_number, to sort them).
Here's an example based on Scott's emp table. This is its contents:
SQL> select empno, ename, job, sal
2 from emp
3 order by ename;
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7876 ADAMS CLERK 1100
7499 ALLEN SALESMAN 1600
7698 BLAKE MANAGER 2850
7782 CLARK MANAGER 2450
7902 FORD ANALYST 3000
7900 JAMES CLERK 950
7566 JONES MANAGER 2975
7839 KING PRESIDENT 5000
7654 MARTIN SALESMAN 1250
7934 MILLER CLERK 1300
7788 SCOTT ANALYST 3000
7369 SMITH CLERK 800
7844 TURNER SALESMAN 1500
7521 WARD SALESMAN 1250
14 rows selected.
Query you might be interested in looks like this:
SQL> with temp as
2 (select empno, ename, job, sal,
3 row_number() over (order by ename) rn,
4 count(*) over () cnt
5 from emp
6 )
7 select *
8 from temp
9 where rn between cnt/2 and cnt;
EMPNO ENAME JOB SAL RN CNT
---------- ---------- --------- ---------- ---------- ----------
7566 JONES MANAGER 2975 7 14
7839 KING PRESIDENT 5000 8 14
7654 MARTIN SALESMAN 1250 9 14
7934 MILLER CLERK 1300 10 14
7788 SCOTT ANALYST 3000 11 14
7369 SMITH CLERK 800 12 14
7844 TURNER SALESMAN 1500 13 14
7521 WARD SALESMAN 1250 14 14
8 rows selected.
SQL>

how to query a table using name from another column from another table

I am trying to get data from a table where the table name is stored in another table.
I am trying for select query but not able to get the data.
example
t1
table name | some data
t2 - table name is same coming from t1
t2
id | some data
I need to fetch the t2 name first from t1 that I am able to do. but using that query response I am not sure how to fetch the second query as the result is coming from first query.
Any help is appreciated.
Thanks
It'll have to be some kind of "dynamic" SQL. One option is to create a function that returns ref cursor. Here's an example.
This is your t1 table; it contains some table names.
SQL> select * from tname;
ID TABLE_NAME
---------- ---------------
1 emp
2 dept
Function:
SQL> create or replace function f_tab (par_table_name in varchar2)
2 return sys_refcursor
3 is
4 rc sys_refcursor;
5 begin
6 open rc for 'select * from ' || dbms_assert.sql_object_name(par_table_name);
7 return rc;
8 end;
9 /
Function created.
Testing: query selects table names from the tname table and returns their contents:
SQL> select f_tab(t.table_name) result
2 from tname t
3 order by t.id;
RESULT
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17.12.1980 00:00:00 800 20
7499 ALLEN SALESMAN 7698 20.02.1981 00:00:00 1600 300 30
7521 WARD SALESMAN 7698 22.02.1981 00:00:00 1250 500 30
7566 JONES MANAGER 7839 02.04.1981 00:00:00 2975 20
7654 MARTIN SALESMAN 7698 28.09.1981 00:00:00 1250 1400 30
7698 BLAKE MANAGER 7839 01.05.1981 00:00:00 2850 30
7782 CLARK MANAGER 7839 09.06.1981 00:00:00 2450 10
7788 SCOTT ANALYST 7566 09.12.1982 00:00:00 3000 20
7839 KING PRESIDENT 17.11.1981 00:00:00 5000 10
7844 TURNER SALESMAN 7698 08.09.1981 00:00:00 1500 0 30
7876 ADAMS CLERK 7788 12.01.1983 00:00:00 1100 20
7900 JAMES CLERK 7698 03.12.1981 00:00:00 950 30
7902 FORD ANALYST 7566 03.12.1981 00:00:00 3000 20
7934 MILLER CLERK 7782 23.01.1982 00:00:00 1300 10
14 rows selected.
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>

How do I know what action was taken by the MERGE statement (ORACLE)?

Is there a way to find out what action was taken by the MERGE statement in an ORACLE procedure?
For example, if I need to subsequently perform different procedures depending on the action performed (INSERT or UPDATE)
p.s. Forgot to clarify, I am considering a case where the MERGE statement processes exactly one row
Without modifying the table, its hard to capture what has been done. There are solutions out there that add a PL/SQL layer into the MERGE statement to force the execution of PL/SQL function for each row, but that will hurt performance.
If you really need it, an additional column could be added, eg
SQL> create table t as select empno, ename, sal, ' ' tag from scott.emp where empno != 7934;
Table created.
SQL> create table t1 as select empno, ename, sal*5 sal from scott.emp where job = 'CLERK';
Table created.
SQL>
SQL> select * from t;
EMPNO ENAME SAL T
---------- ---------- ---------- -
7369 SMITH 800
7499 ALLEN 1600
7521 WARD 1250
7566 JONES 2975
7654 MARTIN 1250
7698 BLAKE 2850
7782 CLARK 2450
7788 SCOTT 3000
7839 KING 5000
7844 TURNER 1500
7876 ADAMS 1100
7900 JAMES 950
7902 FORD 3000
13 rows selected.
SQL> select * from t1;
EMPNO ENAME SAL
---------- ---------- ----------
7369 SMITH 4000
7876 ADAMS 5500
7900 JAMES 4750
7934 MILLER 6500
SQL>
SQL> merge into t
2 using ( select * from t1) t1
3 on ( t.empno = t1.empno )
4 when matched then
5 update
6 set t.sal = t1.sal, t.tag = 'U'
7 when not matched then
8 insert (t.empno,t.ename,t.sal,t.tag)
9 values (t1.empno,t1.ename,t1.sal,'I');
4 rows merged.
SQL>
SQL> select * from t;
EMPNO ENAME SAL T
---------- ---------- ---------- -
7369 SMITH 4000 U
7499 ALLEN 1600
7521 WARD 1250
7566 JONES 2975
7654 MARTIN 1250
7698 BLAKE 2850
7782 CLARK 2450
7788 SCOTT 3000
7839 KING 5000
7844 TURNER 1500
7876 ADAMS 5500 U
7900 JAMES 4750 U
7902 FORD 3000
7934 MILLER 6500 I
14 rows selected.
I've just used U/I but this column could be (say) a numeric field or similar to handle multiple MERGE's over time.
But most people heading down this route, typically end up using separate INSERT and UPDATE blocks
sql%rowcount tells us how many rows were merged (inserted / updated / deleted). There is no way to separate that count into sub-totals for each action.
For example, if I need to subsequently perform different procedures depending on the action performed
Is this a hypothetical? If not, edit your question to outline your actual situation. But generally, the options are:
replace the MERGE with separate DML statements for each action;
use row-level auditing to track actions (be careful introducing this if you don't already have it).

Oracle randomly shuffling entire rows

have a locations table that contains 3 types of records (see below). I only need to SELECT record_type = 'G' rows.
I want to wrap the data in a loop ( ie 5 times) but how can I randomly shuffle the ENTIRE rows, by location_id, so they appear in a different order with each iteration of the loop? Perhaps, sticking the rows into an array and shuffling the array?
Note, I don't want to shuffle the columns, I want the row kept in tact. In the future more locations maybe added.
CREATE TABLE locations AS
SELECT level AS location_id,
'Door ' || level AS location_name,
CASE round(dbms_random.value(1,3))
WHEN 1 THEN 'A'
WHEN 2 THEN 'T'
WHEN 3 THEN 'G'
END AS location_type
FROM dual
CONNECT BY level <= 25;
SELECT * from tour_detail
Order by tour_id, tour_time
TOUR_ID TOUR_TIME LOCATION_ID
1 06212020 00:10:25 2
1 06212020 00:21:05 18
1 06212020 00:30:33 11
1 06212020 00:40:51 17
1 06212020 00:52:13 4
1 06212020 01:01:42 2
1 06212020 01:07:52 11
We have an access control system when you slide your card it records card_num, location_id, access_date in MMDDYYY HH24:MI:SS format.
Some wanted to create a guard tour system. That compares the access_date to the tour_detail detail date. If within a certain criteria say + or - 2 minutes that means the guard was on time, if GT or LT 2 minutes we log an early or late, if GT Or LT 5 minutes or no access_history record we log a no show.
I was asked to throw together some testing data. As you can see the tour_detail records contain MMDDYYYY HH24:MI:SS so I'm thinking about changing the tour_detail records to interval hour to minute but I have little experience working with that.
so they appear in a different order with each iteration of the loop
This is pointless. You are inserting rows into a table. Rows (in a RDBMS table) aren't stored in any particular order.
But, you can sort them when selecting from that table and - in order to do that - you have to use the order by clause.
As of selecting, see if this helps:
SQL> with data as
2 (select empno, ename, job, sal
3 from emp
4 where deptno = 20
5 order by round(dbms_random.value(0, 15))
6 )
7 select column_value, d.*
8 from data d cross join table(cast(multiset(select level from dual
9 connect by level <= 5
10 ) as sys.odcinumberlist));
COLUMN_VALUE EMPNO ENAME JOB SAL
------------ ---------- ---------- --------- ----------
4 7902 FORD ANALYST 3000
4 7788 SCOTT ANALYST 3000
2 7369 SMITH CLERK 1000
4 7369 SMITH CLERK 1000
1 7369 SMITH CLERK 1000
5 7566 JONES MANAGER 2975
1 7876 ADAMS CLERK 1100
1 7788 SCOTT ANALYST 3000
5 7788 SCOTT ANALYST 3000
3 7876 ADAMS CLERK 1100
1 7902 FORD ANALYST 3000
2 7876 ADAMS CLERK 1100
5 7902 FORD ANALYST 3000
3 7902 FORD ANALYST 3000
3 7566 JONES MANAGER 2975
1 7566 JONES MANAGER 2975
3 7788 SCOTT ANALYST 3000
2 7902 FORD ANALYST 3000
4 7566 JONES MANAGER 2975
5 7876 ADAMS CLERK 1100
2 7788 SCOTT ANALYST 3000
3 7369 SMITH CLERK 1000
4 7876 ADAMS CLERK 1100
2 7566 JONES MANAGER 2975
5 7369 SMITH CLERK 1000
25 rows selected.
SQL>
Let's say you have a table with 5 IDs
create table tab as
select rownum id from dual connect by level <= 5;
THis produce a random order output of the rows
select * from tab order by dbms_random.value;
ID
----------
2
4
5
1
3
If you repeat the statment you get a different (random) result
ID
----------
5
4
2
1
3
So simple repaet the query in the loop or make something like this
with data as (
select 1 pass_id, id from tab
union all
select 2 pass_id, id from tab
union all
select 3 pass_id, id from tab )
select * from data
order by 1,dbms_random.value

sql-oHow can I achieve the following in oracle without creating a stored procedure?group by using

how can ı get this result with using employees table?for salary< average 0
for salary >avg 1
employee_id salary
a 1
b 1
c 0
d 0
e 0
f 1
You could simply calculate the AVERAGE of the salary and compare it with the SALARY for each employee in the CASE expression itself.
For example,
SQL> SELECT empno,
2 ename,
3 CASE
4 WHEN sal >
5 (SELECT AVG(sal) FROM emp
6 )
7 THEN 1
8 ELSE 0
9 END flag
10 FROM emp;
EMPNO ENAME FLAG
---------- ---------- ----------
7369 SMITH 0
7499 ALLEN 0
7521 WARD 0
7566 JONES 1
7654 MARTIN 0
7698 BLAKE 1
7782 CLARK 1
7788 SCOTT 1
7839 KING 1
7844 TURNER 0
7876 ADAMS 0
7900 JAMES 0
7902 FORD 1
7934 MILLER 0
14 rows selected.
SQL>
Alternate solution You could use AVG() analytic function.
For example,
SQL> SELECT empno,
2 ename,
3 CASE
4 WHEN sal > avg(sal) OVER(ORDER BY NULL) THEN
5 1
6 ELSE
7 0
8 END flag
9 FROM emp;
EMPNO ENAME FLAG
---------- ---------- ----------
7369 SMITH 0
7499 ALLEN 0
7521 WARD 0
7566 JONES 1
7654 MARTIN 0
7698 BLAKE 1
7782 CLARK 1
7788 SCOTT 1
7839 KING 1
7844 TURNER 0
7876 ADAMS 0
7900 JAMES 0
7902 FORD 1
7934 MILLER 0
14 rows selected.
SQL>
You could cross join on the average salary:
SELECT employee_id,
CASE WHEN salary > avg_salary THEN 1 ELSE 0 END AS salary
FROM employee
CROSS JOIN (SELECT AVG(salary)
FROM employee)