问题
I have linked together groups of records which are related via a shared parent. Unfortunately there are some quite complicated family groups, and it's become clear that just using a shared parental relationship isn't enough - I also want to take into account sibling relationships.
To be clear - this is actual family groups, which are currently identified if their is a shared mother or father relationship, but in some cases a child may not share parents with another child who is still linked to them via a sibling.
So in the above example, Lou has no shared parental relationship with Stacey, but Stacey is the sister of Nate who is the brother of Deb who is the sister of Lou, which links them together.
For arguments sake let's say we have some SQL like this:
SELECT A.ID, A.SIBS FROM A
Which produces a dataset like this:
ID SIBS
A B
A C
B A
C A
C D
D C
I would like to produce a table from the above dataset which takes into account siblings of siblings - for example sibling C is related to sibling D and Sibling A, but is related to sibling B via sibling A. The resulting table would look like this:
ID SIBS
A B
A C
A D
B C
B D
B A
C A
C D
C B
D C
D A
D B
Any advice would be appreciated.
回答1:
Its unclear whether the relationships are reflexive (i.e. if B
is a "sibling" of A
then A
is a "sibling" of B
) as you have some duplicate rows with the reversed relationships in your data and some where this property is not evident.
Assuming that your relationships are not reflexive then:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE A ( ID, SIBS ) AS
SELECT 'A', 'B' FROM DUAL UNION ALL
SELECT 'A', 'C' FROM DUAL UNION ALL
SELECT 'B', 'A' FROM DUAL UNION ALL
SELECT 'C', 'A' FROM DUAL UNION ALL
SELECT 'C', 'D' FROM DUAL UNION ALL
SELECT 'D', 'C' FROM DUAL UNION ALL
SELECT 'E', 'F' FROM DUAL UNION ALL
SELECT 'F', 'G' FROM DUAL UNION ALL
SELECT 'G', 'H' FROM DUAL;
Query 1:
SELECT DISTINCT
CONNECT_BY_ROOT( ID ) AS ID,
SIBS
FROM A
WHERE CONNECT_BY_ROOT( ID ) <> SIBS
CONNECT BY NOCYCLE
PRIOR SIBS = ID
ORDER BY ID, SIBS
Results:
| ID | SIBS |
|----|------|
| A | B |
| A | C |
| A | D |
| B | A |
| B | C |
| B | D |
| C | A |
| C | B |
| C | D |
| D | A |
| D | B |
| D | C |
| E | F |
| E | G |
| E | H |
| F | G |
| F | H |
| G | H |
Query 2: If they are reflexive then you can use UNION [ALL]
to duplicate the table with the relationships in the reverse direction and then use the previous technique:
SELECT DISTINCT
CONNECT_BY_ROOT( ID ) AS ID,
SIBS
FROM (
SELECT ID, SIBS FROM A
UNION
SELECT SIBS, ID FROM A
)
WHERE CONNECT_BY_ROOT( ID ) <> SIBS
CONNECT BY NOCYCLE
PRIOR SIBS = ID
ORDER BY ID, SIBS
Results:
| ID | SIBS |
|----|------|
| A | B |
| A | C |
| A | D |
| B | A |
| B | C |
| B | D |
| C | A |
| C | B |
| C | D |
| D | A |
| D | B |
| D | C |
| E | F |
| E | G |
| E | H |
| F | E |
| F | G |
| F | H |
| G | E |
| G | F |
| G | H |
| H | E |
| H | F |
| H | G |
回答2:
As an alternative to a hierarchical query, you coudl also use recursive subquery factoring:
with r (pno, sibs) as (
select a.id, a.sibs
from a
union all
select r.pno, a.sibs
from r
join a on a.id = r.sibs
)
cycle pno, sibs set is_cycle to 1 default 0
select distinct pno, sibs
from r
where pno != sibs
order by pno, sibs;
PNO SIBS
--- ----
A B
A C
A D
B A
B C
B D
C A
C B
C D
D A
D B
D C
The anchor member gets the raw data from your table. The recursive member joins each row found so far back to your main table, keeping the original pno
(as the equivalent to connect_by_root(id)
).
The hierarchical query is likely to perform better, I think, but it depends a bit on your data, so you could try both approaches.
来源:https://stackoverflow.com/questions/53616010/oracle-sq-identify-siblings-via-siblings