How to (recursively) update a table based on available hierarchy? - sql

Given these 3 column values, how can i update a table with the top level head for each employee?
| EmpID | EmpName | SupervisorID | DeptHeadID |
|:-----:|:-------:|:------------:|:----------:|
| 3 | Adam | null | |
| 1 | Sam | 5 | |
| 6 | Mike | 2 | |
| 5 | Jack | 3 | |
| 2 | Steph | 5 | |
| 8 | Rob | 2 | |
The result should be like this
| EmpID | EmpName | SupervisorID | DeptHeadID |
|:-----:|:-------:|:------------:|:----------:|
| 3 | Adam | null | null |
| 1 | Sam | 5 | 3 |
| 6 | Mike | 2 | 3 |
| 5 | Jack | 3 | 3 |
| 2 | Steph | 3 | 3 |
| 8 | Rob | 2 | 3 |

Example
;with cteP as (
Select EmpID,SupervisorID,TopLvl=EmpID
From YourTable
Where SupervisorID is null
Union All
Select r.EmpID,r.SupervisorID,TopLvl=p.TopLvl
From YourTable r
Join cteP p on r.SupervisorID = p.EmpID)
Update YourTable
set DeptHeadID = nullIf(TopLvl,A.EmpID)
From YourTable A
Join cteP B on A.EmpID=B.EmpID
-- Show Updated Table
Select * From YourTable
Updated Table
EmpID EmpName SupervisorID DeptHeadID
3 Adam NULL NULL
1 Sam 5 3
6 Mike 2 3
5 Jack 3 3
2 Steph 5 3
8 Rob 2 3

Related

Get Field Hierachy

I have the following tables and I want to get the quantity of users by country:
+--------+------+:
| user | zone |
+--------+------+
| Paul | 7 |
+--------+------+
| John | 5 |
+--------+------+
| Peter | 6 |
+--------+------+
| Frank | 5 |
+--------+------+
| Silvia | 2 |
+--------+------+
| Carl | 4 |
+--------+------+
| Mark | 3 |
+--------+------+
Regions
+---------+-----------------+----------+--+
| zone_id | zone_name | idUpzone | |
+---------+-----------------+----------+--+
| 1 | Global | null | |
+---------+-----------------+----------+--+
| 2 | US | 1 | |
+---------+-----------------+----------+--+
| 3 | Florida | 2 | |
+---------+-----------------+----------+--+
| 4 | Orlando | 3 | |
+---------+-----------------+----------+--+
| 5 | China | 1 | |
+---------+-----------------+----------+--+
| 6 | Orlando Sector | 4 | |
+---------+-----------------+----------+--+
| 7 | Beijing | 5 | |
+---------+-----------------+----------+--+
so I get something like this
+---------+-----+
| Country | QTY |
+---------+-----+
| US | 4 |
+---------+-----+
| China | 3 |
+---------+-----+
Use a recursive CTE to get the highest level and then join:
with cte as (
select zone_id, zone_id as top_zone_id, zone_name as top_zone_name, 1 as lev
from regions
where parent_zone_id = 1
union all
select r.zone_id, cte.top_zone_id, top_zone_name, lev + 1
from cte join
regions r
on r.idUpzone = cte.zone_id
)
select cte.top_zone_name, count(*)
from users u join
cte
on u.zone = cte.zone_id
group by cte.top_zone_name;
Try this out:
SELECT
r.zone_name AS Contry, COUNT(*) QTY
FROM (
SELECT * FROM users u
INNER JOIN regions r ON u.zone = r.zone_id
) a
GROUP BY r.zone_name

SQL select records based on not existing record

I have a table with records like this:
ID | NAME | PHASE_ID | OPERATION_ID | EXT_ID |
---+------+----------+--------------+--------+
1 | john | 5 | 5019 | aa-aaa |
2 | ann | 1 | 5048 | aa-aaa |
3 | mary | 1 | 5048 | bb-bbb |
4 | zack | 5 | 5019 | cc-ccc |
5 | paul | 1 | 5048 | cc-ccc |
6 | zoe | 2 | 5555 | aa-aaa |
7 | luke | 3 | 6666 | bb-bbb |
and I want to select all names from records having PHASE_ID=1 and OPERATION_ID=5048 for which there is no record with the same EXT_ID that have PHASE_ID=5 and OPERATION_ID=5019. In this table there is many combinations of PHASE_ID and OPERATION_ID for the same EXT_ID.
In this table name that fulfill this select would be only mary.
Any help composing that select would be appreciated.
Sounds like not exists:
select distinct name
from t
where phase_id = 1 and operation_id = 5048 and
not exists (select 1
from t t2
where t2.ext_id = t.ext_id and
t2.phase_id = 5 and t2.operation_id = 5019
);

How to remove duplicate values from oracle join?

I want to create a view that present only the results and not present the duplicates, I have 3 tables in oracle database:
The first table contain general information about a person
+-----------+-------+-------------+
| ID | Name | Birtday_date|
+-----------+-------+-------------+
| 1 | Byron | 12/10/1998 |
| 2 | Peter | 01/11/1973 |
| 4 | Jose | 05/02/2008 |
+-----------+-------+-------------+
The second table contain information about a telephone of the people in the first table.
+-------+----------+----------+----------+
| ID |ID_Person |CELL_TYPE | NUMBER |
+-------+- --------+----------+----------+
| 1221 | 1 | 3 | 099141021|
| 2221 | 1 | 2 | 099091925|
| 3222 | 1 | 1 | 098041013|
| 4321 | 2 | 1 | 088043153|
| 4561 | 2 | 2 | 090044313|
| 5678 | 4 | 1 | 092049013|
| 8990 | 4 | 2 | 098090233|
+----- -+----------+----------+----------+
The Third table contain information about a email of the people in the first table.
+------+----------+----------+---------------+
| ID |ID_Person |MAIL_TYPE | Email |
+------+- --------+----------+---------------+
| 221 | 1 | 1 |jdoe#aol.com |
| 222 | 1 | 2 |jdoe1#aol.com |
| 421 | 2 | 1 |xx12#yahoo.com |
| 451 | 2 | 2 |dsdsa#gmail.com|
| 578 | 4 | 1 |sasaw1#sdas.com|
| 899 | 4 | 2 |cvcvsd#wew.es |
+------+----------+----------+---------------+
if i do a inner join with this tables the result will do something like that
+-----+-------+-------------+----------+----------+----------+----------------+
| ID | Name | Birtday_date| CELL_TYPE| NUMBER |MAIL_TYPE|Email |
+-----+-------+-------------+----------+----------+----------+----------------+
| 1 | Byron | 12/10/1998 | 3 | 099141021|1 |jdoe#aol.com |
| 1 | Byron | 12/10/1998 | 3 | 099141021|2 |jdoe1#aol.com |
| 1 | Byron | 12/10/1998 | 2 | 099091925|1 |jdoe#aol.com |
| 1 | Byron | 12/10/1998 | 2 | 099091925|2 |jdoe1#aol.com |
| 1 | Byron | 12/10/1998 | 1 | 098041013|1 |jdoe#aol.com |
| 1 | Byron | 12/10/1998 | 1 | 098041013|2 |jdoe1#aol.com |
| 2 | Peter | 01/11/1973 | 1 | 088043153|1 |xx12#yahoo.com |
| 2 | Peter | 01/11/1973 | 1 | 088043153|2 |dsdsa#gmail.com |
| 2 | Peter | 01/11/1973 | 2 | 090044313|1 |xx12#yahoo.com |
| 2 | Peter | 01/11/1973 | 2 | 090044313|2 |dsdsa#gmail.com |
| 4 | Jose | 05/02/2008 | 1 | 088043153|1 |sasaw1#sdas.com |
| 4 | Jose | 05/02/2008 | 1 | 088043153|2 |cvcvsd#wew.es |
| 4 | Jose | 05/02/2008 | 2 | 088043153|1 |sasaw1#sdas.com |
| 4 | Jose | 05/02/2008 | 2 | 088043153|2 |cvcvsd#wew.es |
+-----+-------+-------------+----------+----------+----------+----------------+
So the result that i will to present in a view is the next
+-----+-------+-------------+----------+----------+----------+----------------+
| ID | Name | Birtday_date| CELL_TYPE| NUMBER |MAIL_TYPE|Email |
+-----+-------+-------------+----------+----------+----------+----------------+
| 1 | Byron | 12/10/1998 | 3 | 099141021|1 |jdoe#aol.com |
| 1 | Byron | 12/10/1998 | | |2 |jdoe1#aol.com |
| 1 | Byron | 12/10/1998 | 2 | 099091925| | |
| 1 | Byron | 12/10/1998 | 1 | 098041013| | |
| 2 | Peter | 01/11/1973 | 1 | 088043153|1 |xx12#yahoo.com |
| 2 | Peter | 01/11/1973 | | |2 |dsdsa#gmail.com |
| 2 | Peter | 01/11/1973 | 2 | 090044313| | |
| 4 | Jose | 05/02/2008 | 1 | 092049013|1 |sasaw1#sdas.com |
| 4 | Jose | 05/02/2008 | | |2 |cvcvsd#wew.es |
| 4 | Jose | 05/02/2008 | 2 | 098090233| | |
+-----+-------+-------------+----------+----------+----------+----------------+
I tried to achieve a similar output using
case
when row_number() over (partition by table1.id order by table2.type) = 1
then table1.value
end
as "VALUE"
But the result is nothing that I expect and some rows they repeats
What you need to do is enumerate the rows and then join on those enumerations. This is tricky, because you don't know how many are in each list. Well, there is another method using conditional aggregation:
select p.id, p.name, p.birthday,
max(cell_type) as cell_type, max(number) as number,
max(mail_type) as mail_type, max(email) as email
from person p left join
((select id_person, cell_type, number,
null as mail_type, null as email,
row_number() over (partition by id_person order by number) as seqnum
from phones
) union all
(select id_person, null as cell_type, null as number,
mail_type, email,
row_number() over (partition by id_person order by email) as seqnum
from emails
)
) pe
on pe.id_person = p.id_person
group by p.id, p.name, p.birthday, pe.seqnum
Hope this helps.
Create table person(ID int ,Name varchar(20), Birtday_date date)
Insert into person values
(1,'Byron' ,'12/10/1998'),
(2,'Peter' ,'01/11/1973'),
(4,'Jose ' ,'05/02/2008')
Create table phones (ID int,ID_Person int,CELL_TYPE int,NUMBER float)
Insert into phones values
(1221, 1 , 3,099141021),
(2221, 1 , 2,099091925),
(3222, 1 , 1,098041013),
(4321, 2 , 1,088043153),
(4561, 2 , 2,090044313),
(5678, 4 , 1,092049013),
(8990, 4 , 2,098090233)
Create table emails(ID int,ID_Person int, MAIL_TYPE int, Email varchar(100))
Insert into emails values
(221, 1 , 1, 'jdoe#aol.com '),
(222, 1 , 2, 'jdoe1#aol.com '),
(421, 2 , 1, 'xx12#yahoo.com '),
(451, 2 , 2, 'dsdsa#gmail.com'),
(578, 4 , 1, 'sasaw1#sdas.com'),
(899, 4 , 2, 'cvcvsd#wew.es ')
select p.id, p.name, p.Birtday_date,
case when Lag(number) over(partition by p.id order by p.id,pe.id) = number then null else cell_type end as cell_type,
case when Lag(number) over(partition by p.id order by p.id,pe.id) = number then null else number end as number,
mail_type as mail_type, email as email
from person p left join
(select pp.ID_Person, cell_type, number, mail_type, email,pp.id from
(select ID_Person, cell_type, number,id,
row_number() over (partition by ID_Person order by id ) as seqnum
from phones
) pp left join
(select ID_Person,
mail_type, email, 1 as seqnum
from emails
)e on pp.ID_Person = e.ID_Person and pp.seqnum = e.seqnum
) pe
on pe.ID_Person = p.Id
order by p.id, pe.id

How to structure a proper SQL subquery?

I'm trying to wrap my head around how to do a proper subquery, it's not making sense to me, lets say I have two tables books and chapters:
Books
+----+------------------+----------+---------------------+
| id | name | author | last_great_chapters |
+----+------------------+----------+---------------------+
| 1 | some book title | john doe | 2 |
| 2 | foo novel title | some guy | 4 |
| 3 | other book title | lol man | 3 |
+----+------------------+----------+---------------------+
Chapters
+----+---------+----------------+
| id | book_id | chapter_number |
+----+---------+----------------+
| 1 | 1 | 1 |
| 2 | 1 | 3 |
| 3 | 1 | 4 |
| 4 | 1 | 5 |
| 5 | 2 | 1 |
| 6 | 2 | 2 |
| 7 | 2 | 3 |
| 8 | 2 | 4 |
| 9 | 2 | 5 |
| 10 | 3 | 1 |
| 11 | 3 | 2 |
| 12 | 3 | 3 |
| 13 | 3 | 4 |
| 14 | 3 | 5 |
+----+---------+----------------+
How can I join the two tables, and just print out the number of rows (sorted limit(last_great_chapters)) of the "last_great_chapters" from the books table list for each book?
if I understood correctly, you want to print out table books and last_great_chapters count in Chapters table?
if yes, try it
select b.id, b.name, b.author , b.last_great_chapter, COUNT(c.chapter_number) as rownumbers FROM Books as b
LEFT JOIN Chapters AS C ON c.chapter_number = b.last_great_chapters
group by b.id, b.name, b.author , b.last_great_chapter

SQL query from multiple tables

I am new to SQL, I need some help,
I want to find out confirmed (confirmed=1) reservation date with last name Steven
Can some one help for with the this SQL Query
table_member
+-----------+-----------+
|Member_id | last_name |
+-----------+-----------+
| 1 | David |
| 2 | owen |
| 3 | Tom |
| 4 | Steven |
| 5 | Steven |
| 6 | Steven |
| 7 | Steven |
| 8 | Steven |
| 9 | Steven |
+-----------+-----------+
table_resevation
+-----------+-----------+-----------+-----------+
|resvationID|Member_id | day | month |
+-----------+-----------+-----------+-----------+
| 1 | 1 | 1 | 6 |
| 2 | 1 | 2 | 6 |
| 3 | 2 | 1 | 6 |
| 4 | 4 | 1 | 6 |
| 5 | 4 | 2 | 6 |
| 6 | 5 | 1 | 6 |
| 7 | 5 | 2 | 6 |
| 8 | 7 | 3 | 6 |
+-----------+-----------+-----------+-----------+
Confirm
+-----------+-----------+-----------+
|confirmID |resvationID| confirmed |
+-----------+-----------+-----------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 3 | 0 |
| 4 | 4 | 0 |
| 5 | 5 | 1 |
| 6 | 6 | 1 |
| 7 | 7 | 0 |
| 8 | 8 | 1 |
+-----------+-----------+-----------+
Does something like this work for you?
SELECT
m.Member_ID, m.Last_Name,
r.Day, r.Month,
c.ConfirmID
FROM
table_member m
INNER JOIN
table_reservation r ON r.Member_ID = m.Member_ID
INNER JOIN
Confirm c ON c.resvationID = r.resvationID
WHERE
m.Last_Name = 'Steven' AND c.confirmed = 1
Select c.*, r.*, m.*
from confirm c
left join table_reservation r on c.reservationID = r.reservationID
left join table_member m on m.member_id = r.Member_id
Where c.confirmed = 1
and m.name = "Steven"
I did not test it, but that should point you to the way.
(Edit: second "where" was a typo)
Apply inner join on all table
select day,month from table_reservation inner join table_member on table_member.member_id=table_reservation.member_id
inner join confirm on confirm.reservationid=table_reservation.reservationid where confirm.confirmid=1
and table_member.last_name like 'Steven'