Find ALL the supervisors (direct and indirect) of Bob? - oracle

Im learning about Oracle db and i have this question in my book :
here is the question
so the third note:
Find ALL the supervisors (direct and indirect) of Bob ??
the solution in book like this :
select distinct s.super_name,t.super_name from emp_super s * emp_super t where
s.super_name = t.person_name ;
can any one explain this to me ? i dont understand this solution ?
and any other solution for this ?
Note: my db is oracle .

That looks like a hierarchical query. Would this do?
SQL> with test (person, supervisor) as
2 (select 'bob', 'alice' from dual union all
3 select 'mary', 'susan' from dual union all
4 select 'alice', 'david' from dual union all
5 select 'david', 'mary' from dual
6 )
7 select sys_connect_by_path(supervisor, '->') sv
8 from test
9 start with person = 'bob'
10 connect by person = prior supervisor;
SV
---------------------------------------------------------------------
->alice
->alice->david
->alice->david->mary
->alice->david->mary->susan
SQL>
It starts with "Bob" (as you said that it should), and connects every person with their supervisor (i.e. specifies relationship between parent and child rows in the hierarchy). More about hierarchical queries here.
As of the "solution" you posted: well, it is invalid (instead of * there should have been a ,; but, then again, you'd rather use ANSI JOIN). When it is fixed, it just returns all rows except the first one in your table, so I'm not sure why they call it a "solution".
It joins the same table twice, so it is called a self-join.

I am supposing that you have the following table created with you.
We are going to use the concept of creating a cartesian product as it will contain all the possible combinations.
The tested code is given below:
select t.supervisor from empsuper as s, empsuper as t where s.supervisor=t.person;
The output to it will be:
This is the required output according to your question.

select distinct s.super_name,t.super_name from emp_super s *
emp_super t where
s.super_name = t.person_name ;
This solution will give only two levels of employees
Here when you use * in after from, (you should use a ',' actually), you are actually taking a Cartesian product of the two tables( Here Both tables are the same). Also, s and t represent the same tables. Suppose this is the table:
Cartesian Product of this table with itself:
Cartesian Product of this table with itself with the condition that s.supervisor=t.person:
Now, We can see how it gives us the direct and indirect employees.

select supervisor from emp_super where person='Bob'
UNION
select supervisor from emp_super where person in ( select supervisor from emp_super where person='Bob')
UNION
select supervisor from emp_super where person in (select supervisor from emp_super where person in ( select supervisor from emp_super where person='Bob')
)
UNION
select supervisor from emp_super where person in ( select supervisor from emp_super where person in (select supervisor from emp_super where person in ( select supervisor from emp_super where person='Bob')))

Related

How to write correct left Join of two tables?

I want to join two tables, first table primary key data type is number, and second table primary key data type is VARCHAR2(30 BYTE). How to join both tables.
I tried this code but second tables all values are null. why is that?
SELECT a.act_phone_no,a.act_actdevice,a.bi_account_id, a.packag_start_date, c.identification_number,
FROM ACTIVATIONS_POP a
left JOIN customer c
on TO_CHAR(a.act_phone_no) = c.msisdn_voice
first table
act_phone_no bi_account_id
23434 45345
34245 43556
Second table
msisdn_voice identification_number
23434 321113
34245 6547657
It seems that you didn't tell us everything. Query works, if correctly written, on such a sample data:
SQL> with
2 -- Sample data
3 activations_pop (act_phone_no, bi_account_id) as
4 (select 23434, 45345 from dual union all
5 select 34245, 43556 from dual
6 ),
7 customer (msisdn_voice, identification_number) as
8 (select '23434', 321113 from dual union all
9 select '34245', 6547657 from dual
10 )
11 -- query works OK
12 select a.act_phone_no,
13 a.bi_account_id,
14 c.identification_number
15 from activations_pop a join customer c on to_char(a.act_phone_no) = c.msisdn_voice;
ACT_PHONE_NO BI_ACCOUNT_ID IDENTIFICATION_NUMBER
------------ ------------- ---------------------
23434 45345 321113
34245 43556 6547657
SQL>
What could be wrong? Who knows. If you got some result but columns from the CUSTOMER table are empty (NULL?), then they really might be NULL, or you didn't manage to join rows on those columns (left/right padding with spaces?). Does joining on e.g.
on to_char(a.act_phone_no) = trim(c.msisdn_voice)
or
on a.act_phone_no = to_number(c.msisdn_voice)
help?
Consider posting proper test case (CREATE TABLE and INSERT INTO statements).
You are using Oracle ?
Please check the below demo
SELECT a.act_phone_no, a.bi_account_id, c.identification_number
FROM ACTIVATIONS_POP a
left JOIN customer c
on TO_CHAR(a.act_phone_no) = c.msisdn_voice;
SQLFiddle

Oracle sql query returning all of the values instead limiting the values

full disclosure this is part of a homework question but I have tried 6 different versions and I am stuck.
I am trying for find 1 manager every time the query runs. I.e I put the department id in and 1 name pops out. currently, I get all the names, multiple times. I have tried a nesting with an '=' not nesting, union, intersection, etc. I can get the manager id with a basic query, I just can't get the name. the current version looks like this:
select e.ename
from .emp e
where d.managerid in (select unique d.managerid
from works w, .dept d, emp e1
where d.did=1 and e1.eid=w.eid and d.did=w.did );
I realize its probably a really basic mistake that I am not seeing - any ideas?
Its not clear what do you mean get 1 menager any time. are it should be different menagers any time or the same?
Lest go throw your query:
you select all empolyes from table emp where manager_id in next query dataset
You get all managers for dep=1. The rest of tables and conditions are not influent on result dataset.
I theing did is primary key for table dept, If so your query may be rewritten to
select e.ename
from emp e
where d.managerid in (select unique d.managerid
from dept d
where d.did=1);
but this query return to you all emploees and not manager from dept=1
and if you need a manager. you should get emploee who is a manager. If eid is primary key of employee, and managerid is id from employee table you need something like:
select e.ename
from emp e
where e1.eid in (select unique d.managerid
from dept d
where d.did=1);

Give priority to specific row while fetching via select

I have a table with following data
Code Dest
atp ananthapur
blr bangalore
chn chennai
del delhi
hmp himachal
hyd hyderbad
goa goa
I need a query to fetch data such that the rows for codes 'hyd' and 'blr' would remain on top always, and then rest of the rows will follow.
I have done this as below -
select code,dest from (
select 1 rown,a.* from LOCATION a
where a.code in ('hyd','blr')
union
select 2 rown,b.* from LOCATION b
where b.code not in ('hyd','blr')
)
order by rown
I can think of doing it in some other ways also. But my question is -
Is there any oracle defined feature to accomplish this?
Yes, Oracle has such a feature, it's the ORDER BY expressions-list clause.
Try:
SELECT * FROM LOCATION a
ORDER BY
CASE WHEN a.code in ('hyd','blr') THEN 1 ELSE 2 END

Need Oracle query for a BOM IMPLOSION or BOM "WHERE-USED" on Oracle 10g

Working with Oracle DB 10g.
I am tasked with writing an Oracle query for a BOM IMPLOSION commonly known as BOM "WHERE-USED." Essentially, given an item or part, I need to provide a list of parent items that contain that item or part, if any.
I have recently coded a BOM EXPLOSION using the following SQL which utilizes the START WITH and CONNECT BY syntax to create a heirarchy downward from a parent item. I found inspiration for the BOM EXPLOSION query at http://www.confluentminds.com/Trainings/SCM/Topic1.1_Ch1_Part5.html
Current BOM EXPLOSION code:
/* BOM EXPLOSION */
select distinct
level,
sys_connect_by_path(msib.segment1, ' / ') as "PATH",
msib2.segment1 as "CHILD ITEM AT LEVEL/PATH"
/*bic.component_item_id,*/
/*msib.inventory_item_id,*/
/*msib2.inventory_item_id*/
from bom.bom_components_b bic,
bom.bom_structures_b bom,
inv.mtl_system_items_b msib,
inv.mtl_system_items_b msib2
where 1=1
and bic.bill_sequence_id = bom.bill_sequence_id
and bic.disable_date is null
and bom.assembly_item_id = msib.inventory_item_id
and bom.organization_id = msib.organization_id
and bic.component_item_id = msib2.inventory_item_id
and bom.organization_id = msib2.organization_id
and bom.organization_id = #### /* organization id here */
and bic.effectivity_date < sysdate
and bom.alternate_bom_designator is null
start with msib.segment1 = '$$$$$$$$$$' /* top parent item here */
connect by nocycle prior bic.component_item_id = msib.inventory_item_id
order by level
Now, I need go from any child item and list all parent items that contain that child item.
I have searched for "oracle bom implosion" and "oracle bom where used" but nothing is obvious to me. The BOM IMPLOSION seems far less straightforward than the BOM EXPLOSION.
Any help or advice is greatly appreciated.
Clint Van Zee
EDIT 02-NOV-2011:
Yes, I want to traverse up a BOM heirarchy and list those items or components where a specified component is used in the bill.
Based on your answer, I looked into the "connect by" relationship and "start with." I think I have a way to do this. I may have been close to the answer without knowing it.
Craig, here is your mock-up with modifications added to prove this out. I also modified the "connect by" and "start with." It {should!} start with the child component and go "upwards" to list those models or components that "use" the specified starting component. To do this, I also removed the "prior" keyword.
with data
as
(
select 'topmodel1' id, 'component1' child_id from dual union all
select 'topmodel1' id, 'component3' child_id from dual union all
select 'component2' id, 'component5' child_id from dual union all
select 'component3' id, 'component4' child_id from dual union all
select 'component4' id, 'component5' child_id from dual union all
select 'component5' id, null child_id from dual union all
select 'topmodel2' id, 'component1' child_id from dual union all
select 'topmodel2' id, 'component5' child_id from dual union all
select 'component5' id, null child_id from dual
)
select distinct
sys_connect_by_path(id, '/') path, child_id, level
from data
start with child_id = 'component5'
connect by id = child_id
order by level
This produces the following result:
PATH CHILD_ID LEVEL
----------- ---------- -----
/component2 component5 1
/component4 component5 1
/topmodel2 component5 1
Looking at the mock data, component 5 is "used by" component2, component4, and topmodel2. So, this change seems to do what I intend. I had to add the dreaded "distinct" as it was traversing the same paths more than once.
I think this is getting me closer.
Here are the minimal changes to my BOM EXPLOSION code to do this in reverse:
START WITH msib.segment1 = '$$$$$$$$$$' /* child item here */
CONNECT BY nocycle msib.inventory_item_id = bic.component_item_id
ORDER BY level
When I apply this change to my SQL script and try it against the actual Oracle data, the CBO is reporting that the cost of searching just one component is out into the stratosphere. So, this method needs tuning.
I am not 100% sure what you are looking for, but it sounds like you are just wanting to climb up the tree instead of down. This is still possible using the hierarchical query, you just need to change your start with and connect by criteria.
So take a simple example:
with data
as
(
select 1 id, 2 child_id from dual union all
select 1 id, 3 child_id from dual union all
select 2 id, 5 child_id from dual union all
select 3 id, 4 child_id from dual union all
select 4 id, 5 child_id from dual union all
select 5 id, null child_id from dual
)
select sys_connect_by_path(id, '/') path, level
from data
start with id = 1
connect by prior child_id = id;
That will give you the child paths starting from the top of the tree. To change this to start at a point in the tree and move upwards, you would simply replace the start with / connect by with something like:
start with id = 4 -- or wherever in the tree you want to start and move upwards
connect by prior id = child_id;
Hopefully that helps. If not, if you could give an example of what you are wanting your output to look like it would be very beneficial.

PL SQL concatenate 2 resultsets

I need to get the result of concatenating 2 similar querys' resulsets. For some reason had to split the original query in 2, both with their corresponding order by clause. Should be something like (this is an oversimplification of the original queries)
Query1: Select name, age from person where age=10
Resultset1:
Person1, 10
Person3, 10
Query2: Select name, age from person where age=20
Resultset1:
Person2, 20
Person6, 20
The expected result:
Person1, 10
Person3, 10
Person2, 20
Person6, 20
I can not simply use Query1 UNION Query2.
Below the 2 original querys:
(#1)
select cp.CP_ID, cpi.CI_DESCRIPCION, cp.CP_CODIGOJERARQUIZADO, cp.CP_ESGASTO as gasto, cp.CP_CONCEPTOPADRE, LEVEL
from TGCCP_ConceptoPagoIng cp
left join tgcci_ConceptoPagoIngIdioma cpi on cpi.CI_IDCONCEPTOPAGOING = cp.CP_ID and cpi.CI_IDIDIOMA = 1
start with ((CP_CONCEPTOPADRE is null) and (**cp.CP_ESGASTO = 1**))
connect by prior cp.CP_ID = cp.CP_CONCEPTOPADRE
order siblings by CP_CODIGOJERARQUIZADO
(#2)
select cp.CP_ID, cpi.CI_DESCRIPCION, cp.CP_CODIGOJERARQUIZADO, cp.CP_ESGASTO as gasto, cp.CP_CONCEPTOPADRE, LEVEL
from TGCCP_ConceptoPagoIng cp
left join tgcci_ConceptoPagoIngIdioma cpi on cpi.CI_IDCONCEPTOPAGOING = cp.CP_ID and cpi.CI_IDIDIOMA = 1
start with ((CP_CONCEPTOPADRE is null) and (**cp.CP_ESGASTO = 2**))
connect by prior cp.CP_ID = cp.CP_CONCEPTOPADRE
order siblings by CP_CODIGOJERARQUIZADO
I think you want a
select * from ( first query )
UNION ALL
select * from ( second query )
Where first query and second query are the queries from above, so you are turning them into subqueries, thus preserving the order by clauses.
OK, well, I'm not fully certain why you need it this way, but if Oracle won't allow you to do a UNION, or it screws up the ordering when you do, I would try creating a pipelined table function.
An example here
Basically, you'd create a procedure that ran both queries, first one, then the other, putting the results of each into the returned dataset.
It looks like you are looking for a MULTISET UNION. Which can only be used from version 10 upwards.
Regards,
Rob.
You could combine your queries as subqueries and do a single order by on the outer query:
select * from (
<query 1 with its order by>
UNION ALL
<query 2 with its order by>
)
order by column1, column2;
Alternatively, you can implement in PL/SQL the equivalent of a sort merge join with two cursors, but that's unnecessarily complicated.
this solution works perfectly:
select * from ( first query )
UNION ALL
select * from ( second query )
I appreciate everyone that have taken the time to answer.
regards.
For your example:
Select name, age from person where age in (10,20)
or
Select name, age from person where age = 10 or age = 20
However I'm guessing this is not what you need :)

Resources