Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
| Author |
Topic |
|
aiken
Aged Yak Warrior
525 Posts |
Posted - 2003-11-23 : 00:12:37
|
Not FK relationships, but relationships among records.For instance, I have a join table that tells me that anna has met bob and chris, and that bob has met darlene and eddie, and that darlene has met francis. I want to query on anna and see that she has 2 firsthand relationships with bob and chris, and 2 second hand relationships with darlene and eddie (through bob), and 1 third hand relationship (anna-bob-darlene-francis).I also want to query on chris and see that he has 1 firsthand relationship (anna), 1 secondhand relationship (bob through anna), and two third hand relationships (darlene and eddie through bob).I'm struggling with two things here: the table structure is a bit weird, with the concept of source and targetTables and sample data:create table users (i int,handle varchar(20))create table rels (i_users_one int,i_users_two int) insert into users (handle) VALUES (1,'anna')insert into users (handle) VALUES (2,'bob')insert into users (handle) VALUES (3,'chris')insert into users (handle) VALUES (4,'darlene')insert into users (handle) VALUES (5,'eddie')insert into users (handle) VALUES (6,'francis')insert into rels (i_users_one,i_users_two) VALUES (1,2)insert into rels (i_users_one,i_users_two) VALUES (1,3)insert into rels (i_users_one,i_users_two) VALUES (2,4)insert into rels (i_users_one,i_users_two) VALUES (2,5)insert into rels (i_users_one,i_users_two) VALUES (4,6)Desired results:For anna:user distance 2 1 3 1 4 2 5 2 6 3For chris:user distance 1 1 2 2 4 3 5 3[/code]Of course, the results would probably be joined back to users, but it's the fundamental counting concept that I'm having trouble with.Thanks!-b |
|
|
nr
SQLTeam MVY
12543 Posts |
Posted - 2003-11-23 : 10:20:36
|
| This is a hierarchy. You'll have to use a loop to get the result.Do a search for trees on this site or look athttp://www.nigelrivett.net/RetrieveTreeHierarchy.html==========================================Cursors are useful if you don't know sql.DTS can be used in a similar way.Beer is not cold and it isn't fizzy. |
 |
|
|
SamC
White Water Yakist
3467 Posts |
Posted - 2003-11-23 : 10:42:10
|
| This may be a good challenge question as the only solution I see looks like brute force. Here it is.I had some trouble keeping things straight with the sample column names. To make it easier for me, I used the followingCREATE TABLE Users (UserID INT,Username VARCHAR (50))CREATE TABLE Rels (UserIDa INT,UserIDb INT )The rows you show in the example don't include Anna's ID along with her relationships. I'm going to take a guess that the rowset you want is:Username, UserIDn as UserID, n as Distancee.g. Username=Anna, Useridn is the ID of each user, n is distance...SELECT U.Username, U1.UserID, 1 as DistanceFROM Users ULEFT OUTER JOIN Rels R1 ON R1.UserIDa = U.UserIDLEFT OUTER JOIN Users U1 ON U1.UserID = R1.UserIDbWHERE U1.UserID IS NOT NULLUNION ALLSELECT U.Username, U2.UserID, 2 as DistanceFROM Users ULEFT OUTER JOIN Rels R1 ON R1.UserIDa = U.UserIDLEFT OUTER JOIN Users U1 ON U1.UserID = R1.UserIDbLEFT OUTER JOIN Rels R2 ON R2.UserIDa = U1.UserIDLEFT OUTER JOIN Users U2 ON U2.UserID = R2.UserIDbWHERE U2.UserID IS NOT NULLUNION ALLSELECT U.Username, U3.UserID, 3 as DistanceFROM Users ULEFT OUTER JOIN Rels R1 ON R1.UserIDa = U.UserIDLEFT OUTER JOIN Users U1 ON U1.UserID = R1.UserIDbLEFT OUTER JOIN Rels R2 ON R2.UserIDa = U1.UserIDLEFT OUTER JOIN Users U2 ON U2.UserID = R2.UserIDbLEFT OUTER JOIN Rels R3 ON R3.UserIDa = U2.UserIDLEFT OUTER JOIN Users U3 ON U3.UserID = R3.UserIDbWHERE U3.UserID IS NOT NULLIf you want the recordset orderedSELECT Username, UserID, DistanceFROM (SELECT U.Username, U1.UserID, 1 as DistanceFROM Users ULEFT OUTER JOIN Rels R1 ON R1.UserIDa = U.UserIDLEFT OUTER JOIN Users U1 ON U1.UserID = R1.UserIDbWHERE U1.UserID IS NOT NULLUNION ALLSELECT U.Username, U2.UserID, 2 as DistanceFROM Users ULEFT OUTER JOIN Rels R1 ON R1.UserIDa = U.UserIDLEFT OUTER JOIN Users U1 ON U1.UserID = R1.UserIDbLEFT OUTER JOIN Rels R2 ON R2.UserIDa = U1.UserIDLEFT OUTER JOIN Users U2 ON U2.UserID = R2.UserIDbWHERE U2.UserID IS NOT NULLUNION ALLSELECT U.Username, U3.UserID, 3 as DistanceFROM Users ULEFT OUTER JOIN Rels R1 ON R1.UserIDa = U.UserIDLEFT OUTER JOIN Users U1 ON U1.UserID = R1.UserIDbLEFT OUTER JOIN Rels R2 ON R2.UserIDa = U1.UserIDLEFT OUTER JOIN Users U2 ON U2.UserID = R2.UserIDbLEFT OUTER JOIN Rels R3 ON R3.UserIDa = U2.UserIDLEFT OUTER JOIN Users U3 ON U3.UserID = R3.UserIDbWHERE U3.UserID IS NOT NULL) XORDER BY Username, Distance, UserID -- Distance order by user |
 |
|
|
aiken
Aged Yak Warrior
525 Posts |
Posted - 2003-12-03 : 20:00:53
|
| Sorry to be slow responding -- got swamped there and then was away for thanksgiving.Nigel, isn't treating this as a tree or hierarchy going to have problems with there are cross links? For instance, in my example, francis might have met anna. SamC, that's close to working for me, but it doesn't seem to work bidirectionally; that is, if finds all the links when there is a relationship with UserIDa = x, but not when UserIDb = x. Hopefully that makes sense.That difficulty makes me wonder if there isn't some better way of expressing the relationship than arbitrarily calling one ID "a" and the other "b". I think I see how the data could first be massaged so that every link appears both ways, but isn't that needlessly complicating things and probably indicative of a fundamental bit of stupidity on my part?Thanks for the excellent advice, as always. I learn a lot here.Cheers-b |
 |
|
|
ehorn
Master Smack Fu Yak Hacker
1632 Posts |
Posted - 2003-12-03 : 21:25:52
|
I am seeing this as finding how many nodes away a given i isfrom every other i in the tree. In your sample outputFor chris = 3user distance 1 1 2 2 4 3 5 3 francis is not represented at a distance of 4?Should he be present in the output or am I misunderstandingor are you only searching for nodes up to 3 away.The following code will give you nodes distances for all nodes != ideclare @node int set @node = 4 --Defines ideclare @lev int; set @lev=0create table #t (lev int, node int)insert into #t select @lev, @node--down the treewhile @lev<=(select max(i) from users) begin insert into #t select @lev, i_users_one from rels where i_users_two in (select node from #t where lev=@lev-1) set @lev=@lev+1 end--up the treeset @lev=0while @lev<=(select max(i) from users) begin insert into #t select @lev, i_users_two from rels where i_users_one in (select node from #t where lev=@lev-1) set @lev=@lev+1 endselect i,handle,(select top 1 min(distance) from #t t where t.node=a.i) distancefrom( select t.node i, u.handle ,t.lev distance from users u join #t t on t.node=u.i where t.node <> @node) agroup by i,handleorder by distance,idrop table #t |
 |
|
|
SamC
White Water Yakist
3467 Posts |
Posted - 2003-12-03 : 22:42:41
|
| SELECT U.Username, U1.UserID, 1 as DistanceFROM Users ULEFT OUTER JOIN Rels R1 ON R1.UserIDa = U.UserID OR R1.UserIDb = U.UserIDLEFT OUTER JOIN Users U1 ON U1.UserID = R1.UserIDb OR U1.UserID = R1.UserIDaWHERE U1.UserID IS NOT NULL AND U1.UserID <> U.UserIDUNION ALLSELECT U.Username, U2.UserID, 2 as DistanceFROM Users ULEFT OUTER JOIN Rels R1 ON R1.UserIDa = U.UserID OR R1.UserIDb = U.UserIDLEFT OUTER JOIN Users U1 ON U1.UserID = R1.UserIDb OR U1.UserID = R2.UserIDbLEFT OUTER JOIN Rels R2 ON R2.UserIDa = U1.UserID OR R2.UserIDb = U1.UserIDLEFT OUTER JOIN Users U2 ON U2.UserID = R2.UserIDb OR U2.UserID = R2.UserIDaWHERE U2.UserID IS NOT NULL AND U.UserID <> U1.UserIDAND U1.UserID <> U2.UserIDI wonder if doubling up the join conditions solves your problem? |
 |
|
|
|
|
|
|
|