Determine hierarchical relationship, but in reverse - sql

Consider the following table in Oracle
sortOrder thisID levelNo
------------------- ------------------- ---------------------
1 A 0
2 B 1
3 C 1
4 D 2
5 E 3
6 F 3
7 G 1
8 H 0
9 I 1
Which could be seen visually as
A
B
C
D
E
F
G
H
I
How could I determine the child parent relationship, to output the following below? The relationship is based on the sortOrder and levelNo.
thisID parentID
------------------- ---------------------
A A
B A
C A
D C
E D
F D
G A
H H
I H
I am familiar with using queries to determine the level based on a hierarchical parent-child relationship, but haven't figured out a way to do it in reverse.

Please try
SELECT
T.thisID
, CASE T.levelNo
WHEN 0
THEN T.thisID
ELSE (
SELECT thisID FROM Table1
WHERE sortOrder = (
SELECT MAX(sortOrder) FROM Table1
WHERE (levelNo = T.levelNo - 1)
AND sortOrder < T.sortOrder
)
)
END parent
FROM Table1 T
ORDER BY sortOrder;
See it in action: SQL Fiddle.
Please comment if and as further detail / adjustment is required.

Related

SQL parent child hierarchy level columns

I need to transform this:
PositionID
ReportsToID
A
B
A
C
B
D
C
E
D
Into this:
PositionID
ReportsToID
Level 1 ID
Level 2 ID
Level 3 ID
Level 4 ID
Level 5 ID
A
A
B
A
A
B
C
B
A
B
C
D
C
A
B
C
D
E
D
A
B
C
D
E
I am a complete SQL novice and no idea how to tackle this... Any help would be greatly appreciated!
Googling for code that has done this already - have not found any
I decided to write a generic solution for unlimited levels. It's possible to walk all the levels using a recursive CTE.
For example:
with recursive
n as (
select position_id, reports_to_id, reports_to_id as rti,
cast(position_id as varchar) as pt
from t
union all
select n.position_id, n.reports_to_id, t.reports_to_id,
t.position_id || ' < ' || n.pt
from n
join t on n.rti = t.position_id
)
select position_id, reports_to_id, pt from n where rti is null
Result:
position_id reports_to_id pt
------------ -------------- -----------------
A null A
B A A < B
C B A < B < C
D C A < B < C < D
E D A < B < C < D < E
See running example at db<>fiddle.
If you need a static solution with a specific number of columns, it can be done with multiple unioned-queries, each one with an increasing number of joins.

How to output every combination of values in 2 columns, in groups? - SQL

I have a table where I want every parent to be mapped to every child within each group.
Input:
group_id parent child
1 A E
1 B
2 C F
2 D
2 E G
3 X
3 Y
Output:
group_id parent child
1 A E
1 B E
2 C F
2 D F
2 E F
2 C G
2 D G
2 E G
So within each group, I want every value in parent to map to every value in child. If there are no values in child for a group, I want that group completely omitted (as shown where group_id = 3)
I was originally thinking of using GROUP BY 1, 2 and aggregating by something like MAX(child), but then I came across edge cases in my data where there may be >1 child. I also tried using CROSS JOIN but I'm struggling with getting my desired output. Thanks in advance.
Disclaimer, I don't use Redshift so there may be better options. However, a CROSS JOIN should work. Just grab the DISTINCT parent values for all groups. Then do the same for the child values, and JOIN the two results together
SELECT p.group_id, p.parent, c.child
FROM (
SELECT group_id, parent
FROM YourTable
GROUP BY group_id, parent
)
p CROSS JOIN
(
SELECT group_id, child
FROM YourTable
WHERE child IS NOT NULL
GROUP BY group_id, child
)
c
WHERE p.group_id = c.group_id
ORDER BY p.group_id, c.child, p.parent
Results:
group_id
parent
child
1
A
E
1
B
E
2
C
F
2
D
F
2
E
F
2
C
G
2
D
G
2
E
G
db<>fiddle here
You can do it exploiting a variable. The variable will be set to the value of child if its value is not null, otherwise it will copy over the value contained in the variable
SET #child := "";
SELECT
group_id,
parent,
IF(child IS NULL, #child, #child := child) AS child
FROM
tab
Here's a fiddle: https://www.db-fiddle.com/f/5KnoFogV19jc4nWa8rrzVx/0.
Does it work for you?

Creating a VIEW to get a connection count

I have a table below which stores Connections between 2 person
TABLE (CONNECTION)
ID | REQUEST_PERSON | REQUESTEE_PERSON
I would like to build a VIEW which gets the REQUEST_PERSON, REQUESTEE_PERSON and MUTUAL_CONNECTION_COUNT(other common connections count between them). Any help is appreciated
For Example if we have a table data as below
ID | REQUEST_PERSON | REQUESTEE_PERSON
1 A B
2 A C
3 B C
4 D B
5 D A
6 A E
7 B E
8 A F
9 C G
I need a VIEW display below
ID | REQUEST_PERSON | REQUESTEE_PERSON | MUTUAL_CONNECTION_COUNT
1 A B 3
2 A C 1
3 B C 1
4 D B 1
5 D A 1
6 A E 1
7 B E 1
8 A F 0
9 C G 0
This is rather tricky. Here is code that does what you want:
select c.*,
(select count(*)
from (select v.person2
from connections c2 cross apply
(values (c2.REQUESTEE_PERSON, c2.REQUEST_PERSON), (c2.REQUEST_PERSON, c2.REQUESTEE_PERSON)
) v(person1, person2)
where v.person1 IN (c.Request_Person, c.Requestee_Person)
group by v.person2
having count(*) = 2
) v
) in_common
from connections c
order by id;
Here is a SQL Fiddle.
The essence of the problem is finding people who are connected to both people in each row. Your connections are unidirectional, which makes the logic hard to express -- C could be either the first or second person in either connection.
Arrgh!
So, the innermost subquery adds reverse links to the graph. Then, it can focus on filtering by the first person -- who has to match the persons in the outer query. The second person is the one that might be in common.
The inner aggregation is just summarizing by the second person. It filters using having count(*) = 2 to indicate that both people in the outer query need to be connected to the second person in the inner query. The count(*) assumes that you have no duplicates.
Then, these are counted, which is the value you want.

working of the left join

I have an example where in a table there is ID,NAme and M_if(managerID). I populated the table in the following manner
Id Name M_id
1 A 2
2 B NUll
3 C 1
4 D 3
5 E 2
Id is employee ID, Name and M_id is manager ID. In above example A's manager is 2(B), B doesn't have manager, C's manager is 1(A) and so on. I need to find out the names of the employees and their managers name. I have written the following query by doing permutations and combinations which gives me proper result but I am not able to comprehend how exactly the query(left join) is working. Please make me explain the concept.
SELECT (e.Name), ee.name FROM test.employee e
left join test.employee ee on ee.Id = e.M_id
order by e.Id;
result i get
A B
B
C A
D C
E B
Please explain me the joint
two instances are there for same table as :
e
Id Name M_id
1 A 2
2 B NUll
3 C 1
4 D 3
5 E 2
ee
Id Name M_id
1 A 2
2 B NUll
3 C 1
4 D 3
5 E 2
according to your join condition on ee.Id = e.M_id
simply first row of instance e will be selected because of left join and e.M_id will get compared to ee.Id and 2nd row will be selected from second instance of same table.
selection of data from both the table is as :
e.Id e.Name e.M_id | ee.Id ee.Name ee.M_id
1 A 2 | 2 B NUll
2 B NUll |
3 C 1 | 1 A 2
4 D 3 | 3 C 1
5 E 2 | 2 B NUll
that is why it is showing
A B

SQL: Getting the full record with the highest count

I'm trying to write sql that produces the desired result from the data below.
data:
IDNum Opt1 Opt2 Opt3 Count
1 A A E 1
1 A B J 4
2 A A E 9
3 B A F 1
3 B C K 14
4 A A M 3
5 B D G 5
6 C C E 13
6 C C M 1
desired result:
IDNum Opt1 Opt2 Opt3 Count
1 A B J 4
2 A A E 9
3 B C K 14
4 A A M 3
5 B D G 5
6 C C E 13
Essentially I want, for each ID Num, the full record with the highest count. I tried doing a group by, but if I group by Opt1, Opt2, Opt3, this doesn't work because it returns the highest count for each (ID Num, Opt2, Opt3, Opt4) combination which is not what I want. If I only group by ID Num, I can get the max for each ID Num but I lose the information as to which (Opt1, Opt2, Opt3) combination gives this count.
I feel like I've done this before, but I don't often work with sql and I can't remember how. Is there an easy way to do this?
Edit
Prior to op clarifying question for access this would have worked. I am not famillar with access to know if this query would be supported.
I think this will work on SQL Server.
select * from data
inner join (select idnum, max(count) from data
group by idNum )sub
on sub.IdNum=data.IdNum && sub.Count=data.Count
Of course if you have two id's with the same count it would return both rows...
Something like this:
SELECT * FROM table AS t1
JOIN ( SELECT id, max(count) as Id FROM table GROUP BY id ) AS t2
ON t1.id = t2.id AND t1.id = t2.id
This assumes that no idnum has the same max count or you'll get two idnums
Try this query:
SELECT * FROM my_table
GROUP BY IDNum
HAVING Count = MAX(Count)
It should work on Access, but I didn't test it.