2012-02-27 16 views
5

Bir üye-üye bağlantısı tablosum var. Şema member_id, friend_id, is_active. Arkadaşların arkadaşlarının üye bağlantılarının bir listesini oluşturmak istiyorum. Ben yarı-optimize bir şekilde bırakarak, sorgu ile nasıl başa çıkacağından emin değilim.Ayırma Sorguları Derecesi

Yukarıdaki tablo, member_id ve friend_id öğelerinin başka bir tabloda esasen aynı şey olduğu bir şekilde çalışır. Sistemimde, bu kimlikler genellikle bu tablo dışında member_id olarak adlandırılır. Örneğin, benim member_id 21 olduğunu söyleyeyim. Benim numaram, asıl arkadaşlık isteğini orijinal olarak kimin başlattığına dayalı olarak, ya da yedekli veriler istemediğim üyeler ya da friend_id ya da friend_id gibi sonsuz sayıda başka satırda olabilir. Temelde aynı şeyi yapmak için çift sıralarım olurdu.

Sadece bir dereceye kadar derece kuramama olanak tanıyan bir sorguya sahip olmak istiyorum (LinkedIn'i düşünün) ancak aynı zamanda bir kişinin kaç kişi tarafından görüntülenebileceğini de belirleyebilirim (Facebook'u düşünün). Burada x faktörü daha önce bahsettiğim is_active sütundur. Bu sütun 0 veya 1 olabilir. Bu açma/kapama düğmesi gibi davranan basit bir minik sütun. 1 olan arkadaş bağlantıları etkin bir arkadaşlık olurken, 0 beklemede. Bu sorguyu aktif arkadaşlarım ve aktif arkadaşlarımdan ayırmam gerekiyor. Arkadaşlarımın aktif arkadaşlarından hiçbiri benim aktif arkadaşlarım değil.

Böyle bir sorguyu nasıl oluşturabilirim (ayırma düzeyini gösteremesem ve yalnızca karşılıklı bir sayı alsam bile)? Şu anda, bir şey hakkında düşünebiliyorum ama sorgudan sonra sorguları iç içe geçmiş bir döngüyü içeriyor, ve evet, sunucuların genel performansı veya zamanla ilgili sağlık için iyi bir şey olduğunu resmedemiyorum.

+0

Çoğu "kısa yol" algoritmalar ile, tek yönlü bir yol işler daha basit hale getireceğini görünüyor, bu yüzden çok fazla çoğaltma dert etmeyin. –

cevap

5

Arama, JOIN kullanarak, birinci sınıf, en kısa yol araması kullanarak nasıl yapılır. Bu algoritmada sihir yoktur, çünkü cevabımızı bulmak için MySQL kullanıyoruz ve herhangi bir sezgisel keşif veya optimizasyon kullanan hiçbir fantezi algoritması kullanmıyoruz.

'Arkadaşım' tablomun tek yönlü ilişkileri var, bu nedenle '1'den 2'ye' ve '2'den 1'e kadar' saklı olduğu için yinelenenlerimiz var. İşte

veri var: uygulama bariz olacağından ben de is_active hariç ediyorum

member_id friend_id 
1   2 
1   3 
1   4 
2   1 
2   3 
2   5 
2   6 
3   2 
3   1 
4   1 
5   2 
6   2 
6   7 
7   6 
7   8 
8   7 

Biz üye 1 seçili var ve biz soruyoruz 7 ile 1 arkadaşlar, bir arkadaşın arkadaşı olduğunu , vb? 0 sayısı hayır demektir ve 1 sayısı 1 demektir.

SELECT COUNT(*) 
FROM friends f1 
WHERE f1.member_id = 1 
    AND f1.friend_id = 7 

Hayır, o zaman arkadaşın arkadaşı mı?

SELECT COUNT(*) 
FROM friends f1 
JOIN friends f2 
    ON f2.member_id = f1.friend_id 
WHERE f1.member_id = 1 
    AND f2.friend_id = 7 

Hayır, o zaman arkadaşın arkadaşının arkadaşı? 1.

sayısını dönen üçüncü sorgu yolunu '2'ye 1', '2 ila 6' ve '6 ila 7' bulacağını

SELECT COUNT(*) 
FROM friends f1 
JOIN friends f2 
    ON f2.member_id = f1.friend_id 
JOIN friends f3 
    ON f3.member_id = f2.friend_id 
WHERE f1.member_id = 1 
    AND f3.friend_id = 7 

Ve daha neler ...,

Her sorgu daha pahalı hale gelir (daha fazla sayıda ekleme nedeniyle), bu nedenle aramayı bir noktada sınırlamak isteyebilirsiniz. Harika bir şey, bu aramanın her iki uçtan orta noktaya doğru çalışmasıdır ki bu en kısa yol aramaları için önerilen basit bir optimizasyon.

İşte üyesi 1 için olanlar ortak arkadaş öneriler şu şekilde bulabilirsiniz:

SELECT f2.friend_id 
FROM friends f1 
JOIN friends f2 
    ON f2.member_id = f1.friend_id 
LEFT JOIN friends f3 
    ON f3.member_id = f1.member_id 
    AND f3.friend_id = f2.friend_id 
WHERE f1.member_id = 1 
    AND f2.friend_id <> f1.member_id // Not ourself 
    AND f3.friend_id IS NULL // Not already a friend 
+0

Bu, COALESCE ile kombine edildiğinde kullanışlıdır – Darwayne

1

Tabloların özellikleri olmadan aşağıdaki yönergeleri sunabilirim ... ALWAYS sorgunuzu çalıştırırsanız, LOWER ID'yi ilk konuma getirin ve farklı bir kişi yapın (hatta sık karşılaşılan kişinin/sıklığın ne olduğunu görmek için sayın). Diğer taraflara olmak), bloat kaldırmak.

örn:

select 
     case when table.MemberID < table.FriendID 
     then table.MemberID else table.FriendID end as FirstPerson, 
     case when table.MemberID < table.FriendID 
     then table.FriendID else table.MemberID end as SecondPerson 
    from 
    ... 
    where... 

Yani, veri

member ID Friend ID 
1   2 
1   3 
1   4 
2   1 
2   3 
2   5 
3   2 
5   2 

and you queried for friends/associations with member ID 1 you would start with 
1 2 
1 3 
1 4 

but then friendships from ID #2 would return 
1 2 (reversal of 2/1 entry) would be duplicate 
2 3 
2 5 

then from friendship 3 
2 3 (reversal of 3/2 entry) would be duplicate 

then from friendship 5 from member 2 
2 5 (reversal of 5/2 entry) would be dupliate 

emin değil bu tam aradığınız şeydir, ama arkadaşlar/dernek bulma öteki "sosyal ağ" benzer sesler ise . Bir kişinin derneğinden/arkadaşlığından kaç "derece" olursa, muhtemelen sorgularınızı yerleştirmeniz veya en azından bazı döngü yapısından sorgulamaya devam etmeniz gerekir.

+0

Bu, kısmen yararlıdır, daha fazla bilgiye sahip olmanız gerekenden daha fazlasını bilmek ister misiniz? "Sosyal ağ" referansına gelince, kavramda, o kadar sıkı bir nit tipi şey var ki, aynı zamanda sadece öğrenme uğruna öğrenmeye çalışıyorum. – chris

0

ayrıca kabul edilen cevap geliştirmek için, Bu bulduğunu kadar ayrılık her derecesini denetlemek için bir araya yararlanabilirler. örneğin:

SELECT COALESCE( (SELECT 1 FROM friends f1 WHERE f1.member_id = 1 AND f1.friend_id = 7 LIMIT 1), (SELECT 2 FROM friends f1 JOIN friends f2 ON f2.member_id = f1.friend_id WHERE f1.member_id = 1 AND f2.friend_id = 7 LIMIT 1) /*, ..ETC* ) as degrees_away