mysql - Eliminate tuples with reverse relation and no primary Key -
i trying insert relations based on question below. have got far, relations c (as per question below). get, getting records getting "c friends a". far understand question statement "no duplicate friendships", have insert friendship c , not vice-versa. either understand problem wrong, or can't desired result.
so, when try insert table values i've got, result wrong. persons have friends more desired, value of 2.
the structure of tables follows:
friend ( id1, id2 )
the student id1 friends student id2. friendship mutual, if (123, 456) in friend table, (456, 123).
(no primary key) situation trying solve is:
"for cases friends b, , b friends c, add new friendship pair , c. not add duplicate friendships, friendships exist, or friendships oneself."
i have been trying solve problem 2 days now. please help.
thanks in advance.
----my sql query-----
select b.id1 id1,b.id3 id2 (select a.id1 id1,a.id2 id2,a.id3 id3,f3.id2 id4 (select f1.id1 id1,f1.id2 id2,f2.id2 id3 friend f1 join friend f2 on f1.id2=f2.id1 f1.id1<>f2.id2) join friend f3 on a.id3=f3.id1) b b.id1<>b.id4 group b.id1,b.id3
let's assume (for examples) friend table contains these rows.
id1 id2 --- --- b c b b c b d c b
first, start identifying 'full friend' tuples friend table, query this:
select fa.id1 , fa.id2 friend fa join friend fb on fb.id1 = fa.id2 , fb.id2 = fa.id1 fa.id1 fa.id2 ------ ------ b b b c c b
this result shows friends b, , b friends c. (a,c)
, (b,d)
rows omitted because there no inverse, (c,a)
or (d,b)
.
for time being, we'll refer set "ft
" (friend tuples). can write query against set (ft), "a->b->c" , "c->b->a" friend pairs.
select fx.id1 , fy.id2 ft fx join ft fy on fy.id1 = fx.id2 , fy.id2 <> fx.id1 fx.id1 fy.id2 ------ ------ c c
but, need sure don't duplicate rows in friend table, use not in or not exists predicate, or can use anti-join pattern, eliminate rows match row in friend table.
select fx.id1 , fy.id2 ft fx join ft fy on fy.id1 = fx.id2 , fy.id2 <> fx.id1 -- eliminate rows match left join friend fe on fe.id1 = fx.id1 , fe.id2 = fy.id2 fe.id1 null fx.id1 fy.id2 ------ ------ c
now, can replace references ft
query (as inline view) produces set:
select fx.id1 , fy.id2 ( select fa.id1 , fa.id2 friend fa join friend fb on fb.id1 = fa.id2 , fb.id2 = fa.id1 ) fx join ( select fc.id1 , fc.id2 friend fc join friend fd on fd.id1 = fc.id2 , fd.id2 = fc.id1 ) fy on fy.id1 = fx.id2 , fy.id2 <> fx.id1 -- eliminate rows match left join friend fe on fe.id1 = fx.id1 , fe.id2 = fy.id2 fe.id1 null group fx.id1 , fy.id2
(i'm thinking long guaranteed (id1,id2) unique, query won't generate duplicates. , i'm thinking query generate matches specified, , not matches. additional test cases in order confirm. if query produce duplicates, adding group fx.id1, fy.id2
query eliminate them.)
finally, put rows friend table, precede query with:
insert friend (id1,id2)
update
the result want returned depends on how "friendship" represented.
i assuming "friend" pair represented in friend
table existence of 2 tuples: both (a,b)
, (b,a) have exist. (a friendship formed when "a friends b", , "b friends a).
if 1 of rows exists, it's not real friendship, halfway friendship.
i ran several test cases. it's kind of tedious working through them. expanded query adding order rows in deterministic order, , adding additional columns in select list, verify "path" (shared friend). commented out clause, see potential friends.
i did find needed add group by
eliminate duplicates. can derive a-c
friendship 2 or more shared friends e.g. b
, r
. both a-b + b-c
, a-r + r-c
result in a-c
.
this final query tested. it's equivalent previous, except addition of group by.
select fx.id1 , fy.id2 -- , fx.id1>fy.id2 d -- , fx.id1 x1 -- , fx.id2 x2 -- , fy.id1 y1 -- , fy.id2 y2 -- , fe.id1 e1 -- , fe.id2 e2 ( select fa.id1 , fa.id2 , fa.id1>fa.id2 d friend fa join friend fb on fb.id1 = fa.id2 , fb.id2 = fa.id1 -- order -- least(fa.id1,fa.id2) -- , greatest(fa.id1,fa.id2) -- , fa.id1>fa.id2 ) fx join ( select fc.id1 , fc.id2 friend fc join friend fd on fd.id1 = fc.id2 , fd.id2 = fc.id1 -- order -- least(fc.id1,fc.id2) -- , greatest(fc.id1,fc.id2) -- , fc.id1>fc.id2 ) fy on fy.id1 = fx.id2 , fy.id2 <> fx.id1 -- eliminate rows match existing row left join friend fe on fe.id1 = fx.id1 , fe.id2 = fy.id2 fe.id1 null group fx.id1 , fy.id2 order least(fx.id1,fy.id2) , greatest(fx.id1,fy.id2) , fx.id1>fy.id2
if full friendship represented existence of 1 tuple "(a,b)" implies "(b,a)", query need changed.
the inline view query fx
, fy
need expanded return "missing" inverse tuples... if (a,b) in friend table, our query needs return both (a,b) , (b,a). we'd accomplish doing union operation between 2 identical queries, order of columns in select list reversed. (here, make use of union instead of union eliminate duplicates.) inline view query fx
, fy
like:
select fa.id1, fa.id2 ... union select fa.id2, fa.id1 ...
the check eliminate matching rows in friend table need changed (we'd want eliminate both (a,b) , (b,a) resultset if found existing (a,b) or (b,a) row)
on ( fe.id1 = fx.id1 , fe.id2 = fy.id2 ) or ( fe.id1 = fy.id2 , fe.id2 = fx.id1 )
and select list , group need changed eliminate "extra" inverse tuple. use expression in order
select least(fx.id1,fy.id2) id1 , greatest(fx.id1,fy.id2) id2 ... group least(fx.id1,fy.id2) , greatest(fx.id1,fy.id2)
Comments
Post a Comment