4

I'm working on a social web-site project and I need to list "First, Second and Third Degree Contacts" of my contacts. I'm using SQL Server AND C#

Assume a contact table like this:

enter image description here

For first degree contact:

  • If gulsah is me then my first degree contacts are burak,sennur

Query I use to select this:

SELECT contact_2 FROM Contacts_Table WHERE contact_1 like 'gulsah'

For second degree contact:

If gulsah is me again then my second degree contacts are: mali

What makes it difficult is to select contacts of my contacts who are not my first degree contact.

I can select mutual contacts but I guess it is not the right approach.

For example, to select mutual contacts of me (gulsah) and burak:

SELECT contact_1 FROM (SELECT * FROM Contact_Test 
  WHERE contact_2 like 'burak') a
     INNER JOIN (SELECT contact_1 FROM Contact_Test 
     WHERE (contact_2 = 'gulsah')) b 
ON a.contact_1 = b.contact_1

This query works but as I said, it's not the right approach for this job.

For third degree contact:

If gulsah is me again then my third degree contacts are_ mehmet,ahmet

I need to select contacts of my contacts' contacts who are not my first and second degree contact :)

Here is a post from Linkedin which explains contact level.

Thanks for responses.

svick
  • 236,525
  • 50
  • 385
  • 514
Burak F. Kilicaslan
  • 535
  • 2
  • 8
  • 20
  • 2
    Have you looked at the possibility of using [hierarchy functions](http://msdn.microsoft.com/en-us/library/bb677290.aspx) or recursive CTEs (plenty of examples on SO) to accomplish that? Not an answer, just an idea at this point. – dawebber Apr 24 '11 at 05:56
  • Actually, I'm using hierarchy for an infinite sub category system using left and right fields but honestly, I couldn't understand how I can use this method for this question. I guess I need to store some data on contacts table like /1/2/5/ or am I wrong? – Burak F. Kilicaslan Apr 24 '11 at 06:04
  • Not strictly related to your question, but I see two rows: {gulsah,burak} and {burak,gulsah}. Don't they represent the same relationship? – magma Apr 24 '11 at 06:07
  • 1
    See: http://stackoverflow.com/questions/1558290/efficient-way-to-implement-linkedin-like-how-you-are-connected-to-feature – magma Apr 24 '11 at 06:15
  • These two rows are there to check Approval for future development. For example; burak can be in gulsah's list but at the same time, gulsah might not want to be listed in burak's list. I just realized this is a silly approach. Anyway, you can ignore duplicate rows. – Burak F. Kilicaslan Apr 24 '11 at 06:16
  • thanks @magma, I'll examine it. – Burak F. Kilicaslan Apr 24 '11 at 06:19
  • @Burak, @Magma's link is very good. The answer to your question is yes, you do need to store a hierarchyid on each record, which will allow you to look at the specific paths within the tree thus formed. – dawebber Apr 24 '11 at 16:34

3 Answers3

2

What makes it difficult is to select contacts of my contacts who are not my first degree contact.

You could use the EXCEPT operator.

First-degree contacts:

SELECT contact_2 FROM contact WHERE contact_1 = 'gulsah'

Second-degree contacts who are not first-degree contacts:

SELECT
  contactB.contact_2
FROM 
  contact AS contactB
  INNER JOIN contact AS contactA ON contactA.contact_2=contactB.contact_1
WHERE contactA.contact_1 = 'gulsah'
EXCEPT
SELECT contact_2 FROM contact WHERE contact_1 = 'gulsah'

EXCEPT tells SQL server to return all the results from the first SELECT that do NOT appear in the second SELECT.

For third-degree contacts (who are not first- or second-degree contacts):

SELECT
  contactC.contact_2
FROM 
  contact AS contactC
  INNER JOIN contact AS contactB ON contactB.contact_2=contactC.contact_1
  INNER JOIN contact AS contactA ON contactA.contact_2=contactB.contact_1
WHERE contactA.contact_1 = 'gulsah'
EXCEPT
(
SELECT contact_2 FROM contact WHERE contact_1 = 'gulsah'
UNION
SELECT
  contactB.contact_2
FROM 
  contact AS contactB
  INNER JOIN contact AS contactA ON contactA.contact_2=contactB.contact_1
WHERE contactA.contact_1 = 'gulsah'
)

I don't hold high hopes for performance, but of course you will need to check this yourself.


As a side note:

I can select mutual contacts but I guess it is not the right approach.

Use INTERSECT for this.

AakashM
  • 62,551
  • 17
  • 151
  • 186
1

Here's my approach:

  1. Add my contact to a special collected contact list.

  2. For every contact in the collected list as Contact_1 of the contact table, add its corresponding Contact_2 unless that contact is already in the collected list.

  3. Repeat step #2 the number of times that is the target degree number minus one.

  4. Repeat the query in step #2 once more, but this time simply return the result set (do not add the rows to the collected list).

The script:

DECLARE @MyContact varchar(50), @DegreeNumber int;
SET @MyContact = 'gulsah';
SET @DegreeNumber = 3;

DECLARE @CollectedContacts TABLE (Contact varchar(50));
INSERT INTO @CollectedContacts (Contact) VALUES (@MyContact);

WHILE @DegreeNumber > 1 BEGIN
  INSERT INTO @CollectedContacts (Contact)
  SELECT ct.Contact_2
  FROM Contacts_Table ct
    INNER JOIN @CollectedContacts cc ON ct.Contact_1 = cc.Contact
    LEFT JOIN @CollectedContacts cc2 ON ct.Contact_2 = cc2.Contact
  WHERE cc2.Contact IS NULL;

  SET @DegreeNumber = @DegreeNumber - 1;
END;

SELECT ct.Contact_2
FROM Contacts_Table ct
  INNER JOIN @CollectedContacts cc ON ct.Contact_1 = cc.Contact
  LEFT JOIN @CollectedContacts cc2 ON ct.Contact_2 = cc2.Contact
WHERE cc2.Contact IS NULL;

As you can see, both the degree number and 'my' contact are parametrisable. I'm using the varchar type for contacts, but that of course can easily be replaced with int, if needed.

Andriy M
  • 76,112
  • 17
  • 94
  • 154
0

maybe this helps: http://techportal.ibuildings.com/2009/09/07/graphs-in-the-database-sql-meets-social-networks/

NickD
  • 2,672
  • 7
  • 37
  • 58
  • @Snoopy, this article is really good and magma also shared this link. I examined it carefully and I think this is one of the perfect solutions for my situation (except social graph databases). But in this article, the examples are written for PostgreSQL so I wasn't able to run it in MSSQL. For example this query wont work on sql; "WITH RECURSIVE transitive_closure(a, b, distance, path_string) A..." – Burak F. Kilicaslan Apr 24 '11 at 14:58
  • @Burak: SQL Server 2005 supports recursive common table expressions as well. I just doesn't use the ANSI standard syntax where the RECURSIVE keyword is required. If you leave out the `RECURSIVE` keyword, it should work with SQL Server as well. –  Apr 24 '11 at 15:09
  • @Burak: the problem with the model is, that it contains endless cycles (e.g. gulsah -> burak -> sennur -> gulsah) which cannot easily be detected using a recursive common table expression... –  Apr 24 '11 at 15:27
  • then what do you recommend? Which method should I follow? This article (graph link) is really a good example of how Linkedin works and that's the exact thing I need. I need to find relations between 2 random person. Instead of using recursive common table expression, what should I do? graph dbs? ([neo4j](http://neo4j.org/)) – Burak F. Kilicaslan Apr 24 '11 at 15:48
  • @a_horse_with_no_name: what do you think about this: http://stackoverflow.com/questions/1192945/sql-server-2005-recursive-query-with-loops-in-data-is-it-possible (see the comment from Jose Chama) – NickD Apr 24 '11 at 20:38
  • @Snoopy: yes, remembering the full path to an item seems to be the only way to detect an infinite loop (at least with SQL Server). But this might not be feasible for large results. –  Apr 24 '11 at 20:51