Avatar billede dotdonk Nybegynder
24. august 2006 - 21:53 Der er 26 kommentarer og
1 løsning

Hjælp til sql query?

Hej med jer...

Jeg har et problem jeg kan ikke kan finde en løsning på, håber en af jer har et forslag eller to.

Jeg har 2 tabeller. Den første tabel indeholder brugere. ID, navn etc. Den anden tabel indeholder bruger id'er, for at se om de kender hinanden.

Bruger tabel:

ID  Navn
-------------
1    Per
2    Hans
3    Hanne
4    Niels
5    Karsten
6    Pia


Kontakt tabel:

ID  BrugerID1  BrugerID2
---------------------------------
1        2              5
2        5              2
3        1              5
4        5              1
5        3              2
6        2              3
7        1              4
8        4              1


I kontakt tabellen er bruger id'erne "parret", således at de tilhører hinanden.

Nu kommer det der er tricky.

Case:


ID 2  Hans  - ID 5 Karsten  = kender hinanden

ID 2  Hans  - ID 3 Hanne  = kender hinanden

ID 1  Per    - ID 5 Karsten  = kender hinanden

ID 1  Per    - ID 4 Hanne    = kender hinanden


Hans og Per kender ikke hinanden, men Hans vil have fat i Per igennem Karsten.

Jeg skal nu vise en Hyperlink liste med de navne som Hans kan benytte for at få fat på Per.


ID5 Karsten
ID4 Hanne


Hvis der er nogen her som omsætte det til en COOL sql statement. (Den behøver ikke engang at være cool!)

Jeg kan bare ikke finde en løsning på det.

Håber at høre fra nogen.

Mvh

dotdonk
Avatar billede lorentsnv Nybegynder
24. august 2006 - 22:32 #1
Jeg har ikke nogen elegang løsning til dig, men en måde at komme lidt nærmere løsning på, kunne være at linke kontakt tabel fler gange til sig selv. I så fald må du definere hvor mange led du ønsker at lede igennem:

Select BrugerID2
    , k2.BrugerID2 as BrugerID3
    , k3.BrugerID2 as BrugerID4
    , k4.BrugerID2 as BrugerID5
from Kontakt k1
left join Kontakt k2 on k1.BrugerID2 = k2.BrugerID1
left join Kontakt k3 on k2.BrugerID2 = k3.BrugerID1
left join Kontakt k4 on k3.BrugerID2 = k4.BrugerID1
Avatar billede lorentsnv Nybegynder
24. august 2006 - 22:38 #2
Tanken var at du har en where, hvor du søger efter en bestem udgangsperson. Du får en liste med alle personer som vedkommende kender som første led,
Select BrugerID2
    , k2.BrugerID2 as BrugerID3
    , k3.BrugerID2 as BrugerID4
    , k4.BrugerID2 as BrugerID5
from Kontakt k1
left join Kontakt k2 on k1.BrugerID2 = k2.BrugerID1
left join Kontakt k3 on k2.BrugerID2 = k3.BrugerID1
left join Kontakt k4 on k3.BrugerID2 = k4.BrugerID1
Where k1.BrugerID1 = 2
and ( K1.BrugerID2 = 1
  or K2.BrugerID2 = 1
  or K3.BrugerID2 = 1
  or K4.BrugerID2 = 1)
Avatar billede lorentsnv Nybegynder
24. august 2006 - 22:45 #3
Ovenstående SQL er nem at udbygge til x antal led. Den kan eventuelt også udbygges til at indgå som en sub query i en sql som finder de records som har den ønskede kontakt igennem færrest mulig led.

Det er desværre ikke en COOL sql statement, men i mangel af bedre, er det én metode at komme tættere på en løsning.

I SQL Server 2005 skulle der være nogle nye muligheder for recusive SQL, men jeg har ikke testet dette. En recursive løsning ville være væsentlig mere elegant.
Avatar billede dotdonk Nybegynder
24. august 2006 - 22:51 #4
Hej lorentsnv... tusind tak for dine svar.

I Where clause'n har jeg udskiftet:

Where k1.BrugerID1 = 2

med

Where k1.BrugerID1 = 151

og

and ( K1.BrugerID2 = 163
or K2.BrugerID2 = 163
or K3.BrugerID2 = 163
or K4.BrugerID2 = 163)

da det er de id'er jeg tester med.

Nu må du ikke blive forskrækket, men jeg prøver at teste din query.

Vil lige vise dig outputtet.

Måske du kan forklare mig sammenhængen her også:

BrugerID2 BrugerID3  BrugerID4  BrugerID5 
---------------- ----------- ----------- -----------
1                151        1          163       
1                151        230        163       
1                163        1          151       
1                163        1          163       
1                163        1          165       
1                163        1          164       
1                163        1          207       
1                163        1          208       
1                163        1          215       
1                163        1          216       
1                163        1          218       
1                163        1          199       
1                163        1          204       
1                163        1          225       
1                163        1          226       
1                163        1          227       
1                163        1          228       
1                163        1          231       
1                163        164        163       
1                163        164        1         
1                163        230        151       
1                163        230        163       
1                165        1          163       
1                164        163        1         
1                164        163        164       
1                164        163        230       
1                164        1          163       
1                207        1          163       
1                208        1          163       
1                215        1          163       
1                216        1          163       
1                218        1          163       
1                199        1          163       
1                204        1          163       
1                225        1          163       
1                226        1          163       
1                227        1          163       
1                228        1          163       
1                231        1          163       
168              151        1          163       
168              151        230        163       
167              151        1          163       
167              151        230        163       
166              151        1          163       
166              151        230        163       
202              151        1          163       
202              151        230        163       
206              151        1          163       
206              151        230        163       
210              151        1          163       
210              151        230        163       
211              151        1          163       
211              151        230        163       
230              151        1          163       
230              151        230        163       
230              163        1          151       
230              163        1          163       
230              163        1          165       
230              163        1          164       
230              163        1          207       
230              163        1          208       
230              163        1          215       
230              163        1          216       
230              163        1          218       
230              163        1          199       
230              163        1          204       
230              163        1          225       
230              163        1          226       
230              163        1          227       
230              163        1          228       
230              163        1          231       
230              163        164        163       
230              163        164        1         
230              163        230        151       
230              163        230        163       
225              1          163        1         
225              1          163        164       
225              1          163        230       
225              1          164        163       
225              151        1          163       
225              151        230        163       
No rows affected.
(81 row(s) returned)

:-)
Avatar billede lorentsnv Nybegynder
24. august 2006 - 23:13 #5
Jeg prøver lige at kigge på det. Resultatet er som jeg forventer, og som jeg skriver, skal man nog lave en SQL 'udenom', som forsøger at reducere antal records. Alternativt kan man lave en stored procedure, som først checker første led, så andet led osv.

Hvis ovenstående resultatset i en sp bliver sat ind i en temporær tabel, kan man først cheke om den søgte person findes i første led. Derefer kan man lave en distinct søgning på:

Select distinct BrugerID2, BrugerID3
from temptabel
Where BrugerID3 = 163

Hvis søgt person ikke er i første led, og ovenstående SQL heller ikke give noget resultat, søger man videre i næste led:

Select distinct BrugerID2, BrugerID3, BrugerID4
from temptabel
Where BrugerID4 = 163


Vil en stored proceure være en aktuel løsning?
Avatar billede dotdonk Nybegynder
24. august 2006 - 23:23 #6
Jo jo....  lorentsnv.

Jeg arbejder med SQL Server 2005 og alle sql kald ligger sproc's.

Hvis du vil have 200 point for at løse dette, så vil jeg gerne give dig det, hvis du kan strikke en sql sammen som giver et aktuelt output.

Hordan vil den komplete implementering se ud med en Temp Tbl i sproc'en?
Avatar billede dotdonk Nybegynder
24. august 2006 - 23:25 #7
I øvrigt så er der 2 af de id'er i outputtet ovenfor som har samme kontakt i led nr. 1.

Det er ID 1 og ID 230 som begge kender ID 163.

:-)

Håber ikke det forvirer dig yderligere...
Avatar billede erikjacobsen Ekspert
24. august 2006 - 23:27 #8
Løsningen må nu være en kende simplere. En SELECT der tager hvem A kender, En SELECT der tager hvem B kender, og så en intersection (hedder den INTERSECT ?) mellem de 2. Kan vel klares i en enkelt sætning.
Avatar billede dotdonk Nybegynder
24. august 2006 - 23:33 #9
Lyder også interssesant. Kan du vise en demo sql query?
Avatar billede lorentsnv Nybegynder
25. august 2006 - 08:36 #10
Som nævnt, findes der nok en del forskellige måder at angribe dit problem på. Intersect vil være en god idé, hvis du skal søge igennem ét led, men jeg tror måske du får en udfordring dersom du skal søge igenem mange led.

Kig også på recursive SQL med
WITH common_table_expression (Transact-SQL) 
Søg enten på internet, eler kig her:
http://msdn2.microsoft.com/en-us/library/ms175972.aspx

Jeg har forsøgt at lave en sp til dig:
Create procedure ReturnConnections
    @From as int
  , @To as int

as

Create table #temp(BrugerID2 int, BrugerID3 int, BrugerID4 int, BrugerID5 int)

Insert into #temp
Select BrugerID2
    , k2.BrugerID2 as BrugerID3
    , k3.BrugerID2 as BrugerID4
    , k4.BrugerID2 as BrugerID5
from Kontakt k1
left join Kontakt k2 on k1.BrugerID2 = k2.BrugerID1
left join Kontakt k3 on k2.BrugerID2 = k3.BrugerID1
left join Kontakt k4 on k3.BrugerID2 = k4.BrugerID1
Where k1.BrugerID1 = @From
and ( K1.BrugerID2 = @To
  or K2.BrugerID2 = @To
  or K3.BrugerID2 = @To
  or K4.BrugerID2 = @To)


Declare @Level as int
Set @Level = 0

Select @Level = count(BrugerID2) from #temp where BrugerID2 = @To
If @Level > 0
BEGIN
    Select @To
END
ELSE
BEGIN
  Select @Level = count(BrugerID3) from #temp where BrugerID3 = @To
  IF @Level > 0
  BEGIN
      SELECT DISTINCT BrugerID2 from #temp where BrugerID3 = @To
  END
  ELSE
  BEGIN
      Select @Level = count(BrugerID4) from #temp where BrugerID4 = @To
      IF @Level > 0
      BEGIN
          SELECT DISTINCT BrugerID2, BrugerID3 from #temp where BrugerID4 = @To
      END
  END
END


Denne sp vil returnere 0 records, hvis den ikke finder nogle forbindelser på. Antal kolonne i recordsettet som bliver returneret, afhænger af hvor mange led den skal søge igennem.

Hvis den finder den søgte person i første led, vil den bare returnere ID'en på den søgte person.

Hvis den finder den søgte person i 2. led, vil den returnere ID'en på de(n) person(er) du kan gå igennem.

I begge ovenstående tilfælde, vil du kun få en kolonne i det recordset som bliver returneret.

Hvis du finder din bekendt i 3. led eller senere, vil du få en distinct liste med de mulige veje, men så vil du få 2 eller flere kolonner i dit recordset, afhængig af hvor langt du søger.

Denne sp vil være forholdsvis nem at udbygge til at søge noge ekstra led, men tilpasningen skal laves manuelt.
Avatar billede lorentsnv Nybegynder
25. august 2006 - 08:39 #11
Jeg glemte at checke på det 5. led:

Alter procedure ReturnConnections
    @From as int
  , @To as int

as

Create table #temp(BrugerID2 int, BrugerID3 int, BrugerID4 int, BrugerID5 int)

Insert into #temp
Select BrugerID2
    , k2.BrugerID2 as BrugerID3
    , k3.BrugerID2 as BrugerID4
    , k4.BrugerID2 as BrugerID5
from Kontakt k1
left join Kontakt k2 on k1.BrugerID2 = k2.BrugerID1
left join Kontakt k3 on k2.BrugerID2 = k3.BrugerID1
left join Kontakt k4 on k3.BrugerID2 = k4.BrugerID1
Where k1.BrugerID1 = @From
and ( K1.BrugerID2 = @To
  or K2.BrugerID2 = @To
  or K3.BrugerID2 = @To
  or K4.BrugerID2 = @To)


Declare @Level as int
Set @Level = 0

Select @Level = count(BrugerID2) from #temp where BrugerID2 = @To
If @Level > 0
BEGIN
    Select @To
END
ELSE
BEGIN
  Select @Level = count(BrugerID3) from #temp where BrugerID3 = @To
  IF @Level > 0
  BEGIN
      SELECT DISTINCT BrugerID2 from #temp where BrugerID3 = @To
  END
  ELSE
  BEGIN
      Select @Level = count(BrugerID4) from #temp where BrugerID4 = @To
      IF @Level > 0
      BEGIN
          SELECT DISTINCT BrugerID2, BrugerID3 from #temp where BrugerID4 = @To
      END
      ELSE
      BEGIN
          Select @Level = count(BrugerID5) from #temp where BrugerID5 = @To
          IF @Level > 0
          BEGIN
              SELECT DISTINCT BrugerID2, BrugerID3, BrugerID4 from #temp where BrugerID5 = @To
          END
      END
  END
END
Avatar billede lorentsnv Nybegynder
25. august 2006 - 08:48 #12
Du vil nok gerne have navn og oplysninger om de personer du får ud fra sp'en. Du kan eventuelt udbygge de forskellige select med en link tilbage til Kontakt tbellen, for at få navn med i resultatsettet.
Avatar billede dotdonk Nybegynder
25. august 2006 - 09:35 #13
Morning lorentsnv

Det virker F..... det her!!! Hvordan bliver man så genial til T-SQL?

Jeg har lige præcis brug for deres navn og ID lorentsnv.

Men jeg forstår ikke helt hvordan jeg skal smide dem i de mange selects der.
Avatar billede lorentsnv Nybegynder
25. august 2006 - 10:22 #14
Nedenfor har jeg forsøgt at hente Navn ind fra Bruger tabellen. Jeg viser ikke navn på den bruger du søger (@To), da jeg går ud fra at du har navn på @From og @To. Ellers kan du måske selv udbygge lidt for å få disse navne med.

Da jeg ikke har haft mulighed for at teste sp'en, kan det tenkes at der er skrivefejl i nedenstående:

Alter procedure ReturnConnections
    @From as int
  , @To as int

as

Create table #temp(BrugerID2 int, BrugerID3 int, BrugerID4 int, BrugerID5 int)

Insert into #temp
Select BrugerID2
    , k2.BrugerID2 as BrugerID3
    , k3.BrugerID2 as BrugerID4
    , k4.BrugerID2 as BrugerID5
from Kontakt k1
left join Kontakt k2 on k1.BrugerID2 = k2.BrugerID1
left join Kontakt k3 on k2.BrugerID2 = k3.BrugerID1
left join Kontakt k4 on k3.BrugerID2 = k4.BrugerID1
Where k1.BrugerID1 = @From
and ( K1.BrugerID2 = @To
  or K2.BrugerID2 = @To
  or K3.BrugerID2 = @To
  or K4.BrugerID2 = @To)


Declare @Level as int
Set @Level = 0

Select @Level = count(BrugerID2) from #temp where BrugerID2 = @To
If @Level > 0
BEGIN
    Select * from Bruger where ID = @To
END
ELSE
BEGIN
  Select @Level = count(BrugerID3) from #temp where BrugerID3 = @To
  IF @Level > 0
  BEGIN
      SELECT DISTINCT t.BrugerID2, b2.Navn from #temp t left join Bruger b2 on t.BrugerID2 = b2.ID where BrugerID3 = @To
  END
  ELSE
  BEGIN
      Select @Level = count(BrugerID4) from #temp where BrugerID4 = @To
      IF @Level > 0
      BEGIN
          SELECT DISTINCT t.BrugerID2, b2.Navn as NavnB2, t.BrugerID3, b3.Navn as NavnB3
          FROM      #temp t
          LEFT JOIN Bruger b2 ON t.BrugerID2 = b2.ID
          LEFT JOIN Bruger b3 ON t.BrugerID3 = b3.ID
          WHERE    BrugerID4 = @To
      END
      ELSE
      BEGIN
          Select @Level = count(BrugerID5) from #temp where BrugerID5 = @To
          IF @Level > 0
          BEGIN
              SELECT DISTINCT t.BrugerID2, b2.Navn as NavnB2, t.BrugerID3, b3.Navn as NavnB3, BrugerID4, b4.Navn as NavnB4
              FROM    #temp
              LEFT JOIN Bruger b2 ON t.BrugerID2 = b2.ID
              LEFT JOIN Bruger b3 ON t.BrugerID3 = b3.ID
              LEFT JOIN Bruger b4 ON t.BrugerID4 = b4.ID
              WHERE BrugerID5 = @To
          END
      END
  END
END
Avatar billede lorentsnv Nybegynder
25. august 2006 - 10:23 #15
For god ordens skyld, kunne du includere følgende kode til slut i sp'en:

Drop table #temp
Avatar billede dotdonk Nybegynder
25. august 2006 - 12:24 #16
hej... det virker som det skal.

tusind tak for hjælpen. Hvis du smider et svar, så smider jeg points.
Avatar billede lorentsnv Nybegynder
25. august 2006 - 12:34 #17
svar :)
Avatar billede dotdonk Nybegynder
25. august 2006 - 12:59 #18
hvorfor kan jeg ikke give dig point?
Avatar billede dotdonk Nybegynder
25. august 2006 - 13:14 #19
ahhhh det er en Kommentar du har lagt...
Avatar billede lorentsnv Nybegynder
25. august 2006 - 14:19 #20
hvorfor laver jeg altid den fejl? :o)
Nu er det ihvertfald et svar ;-)
Avatar billede dotdonk Nybegynder
25. august 2006 - 14:45 #21
jeg har lige et sidste lille spm.

jeg har selvfølgelig omdybt feltnavne og "as brugerID1" osv. navne

det hedder nu:

SELECT DISTINCT t.UserConnectionID2 as UserConnectionID, b2.Firstname + ' ' + b2.Lastname as FullName

Når jeg prøver at binde UserConnectionID og FullName til mine asp.net kontroller som løbes igennem i en repeater kontrol, så får jeg en fejl.

Den kan ikke finde UserConnectionID eller FullName som feltnavne.

Meget underligt for jeg har ændret det overalt i querien. Hvad gør jeg forkert her?
Avatar billede dotdonk Nybegynder
25. august 2006 - 14:49 #22
btw

I output vinduet viser den nemlig:

UserConnectionID FullName
-----------------------------
1                Per Hansen
126              Niels Poulsen

så det burde da virke?
Avatar billede lorentsnv Nybegynder
25. august 2006 - 15:20 #23
Jeg har desværre ikke den store erfaring med asp.net.
Avatar billede dotdonk Nybegynder
25. august 2006 - 15:39 #24
hhhmmm... det er lige meget om det er asp.net.

det data man gerne vil vise på sin webside, skal jo trækkes ud og feltnavnene skal på asp siden skal der refereres til.

det gør jeg også, men det er åbenbart ikke de rigtige navne. Men hvorfor er det ikke de rigtige feltnavne, når at den i Q. analyzer viser UserConnectionID og FullName. Så man det da være dem. Det plejer det da at være.

:-)
Avatar billede dotdonk Nybegynder
25. august 2006 - 16:02 #25
med din sproc lorentsnv...

til hvilken felter vil du binde dine data?

BrugerID2  BrugerID3  BrugerID4
NavnB2      NavnB3      NavnB4   

ik?
Avatar billede lorentsnv Nybegynder
25. august 2006 - 22:49 #26
Jeg ved ikke om det giver dig problemer, at proceduren returnerer forskellige antal kolonner, afhængig af afstanden mellem de to personer du søger forbindelserne mellem. Kan du på nogen måde resette feltnavnene i dit ASP.net repeater controll mellem hver gang, og check for mange kolonner proceduren returnerer?
Avatar billede dotdonk Nybegynder
25. august 2006 - 23:20 #27
det har jeg siddet og tænkt på også... hehe.

nej ikke umiddelbart.

Men hvis man på en eller anden måde kunne lave en SELECT som returnere et count på antal felter, inden querien kører videre som den ser ud nu. Så kan jeg i min kode tjekke på det mindste antal og op til det største antal og derefter binde dataene efter antal felter returneret.

Eller kan det laves på en anden måde i SPROC'`n?
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Computerworld tilbyder specialiserede kurser i database-management

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester