RICORSIONE

giovedì 25 gennaio 2007 - 15.44

corkie 12 Profilo | Newbie

Buon giorno a tutti....avrei bisogno di un'aiuto riguardo la ricorsione...
io ho una tabella sql contenente IdUtenti e Idamici messi in questo modo


idutente idamico
1 2
3 4
6 8
2 3

il mio obbiettivo è quello dati due id inseriti in due textbox di sapere se gli id messi a confronto sono amici diretti (es. come 1 e 2 ) o se sono amici nn diretti come (es. 1 è amico di 2, 2 è amico di tre, risultato 1 è amico di 2 che è amico di 3)....come potrei grazie ad un metodo ricorsivo risalire a tutti i possibili collegamenti dati due id????
è un pò difficile ma sicuramente ci sarà qualcuno in grado di aiutarmi????? spero.....grazie anticipatamente...

lbenaglia Profilo | Guru

>idutente idamico
>1 2
>3 4
>6 8
>2 3

Ciao corkie 12,

a fronte di questa tabella, quale result set vorresti ottenere in output?

Ciao!
--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

corkie 12 Profilo | Newbie

ipotesi io metto in input 1 e 3
lui mi deve tornare con risultato
che 1 è amico di 2
2 è amico di tre
capisci che se fosse una tabella da 5000 elementi il risulato sarebbe tutte le coppie che mi portano dall'idutente all'idamico inseriti....
tipo
1 2
3 5
2 4
4 7
8 10
6 3
7 9
se metto in input 1 e 9 il risultato sarà
1 è amico di 2
2 è amico di 4
4 è amico di 7
7 è amico di 9

in somma tutto il percorso (sempre che esista) dall'idutente all'idamico immessi in input.......grazie!!!!

lbenaglia Profilo | Guru

>se metto in input 1 e 9 il risultato sarà
>1 è amico di 2
>2 è amico di 4
>4 è amico di 7
>7 è amico di 9

ma tu in output vuoi che venga prodotto il seguente result set:

x1 è amico di y1
x2 è amico di y2
x3 è amico di y3
x4 è amico di y4

?!?!

--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

corkie 12 Profilo | Newbie

si l'importante che il percorso sia esatto e nel caso non ci sia relazione il risultato sarà "nn c'è relazione"

lbenaglia Profilo | Guru

>si l'importante che il percorso sia esatto e nel caso non ci
>sia relazione il risultato sarà "nn c'è relazione"

Va bene.
Se utilizzi SQL Server 2005 puoi ricorrere ad una Common Table Expression (CTE) ricorsiva per restituire la relazione ed il grado di parentela tra i due ID.
Osserva il seguente esempio:

USE tempdb; CREATE TABLE dbo.Utenti( idutente int NOT NULL PRIMARY KEY, idamico int NOT NULL ); INSERT dbo.Utenti VALUES(1, 2); INSERT dbo.Utenti VALUES(3, 5); INSERT dbo.Utenti VALUES(2, 4); INSERT dbo.Utenti VALUES(4, 7); INSERT dbo.Utenti VALUES(8, 10); INSERT dbo.Utenti VALUES(6, 3); INSERT dbo.Utenti VALUES(7, 9); GO CREATE PROCEDURE dbo.GetAmicizie( @idutente int, @idamico int ) AS WITH Amici(idutente, idamico, Livello) AS ( SELECT idutente, idamico, 0 FROM dbo.Utenti WHERE idutente = @idutente UNION ALL SELECT U.idutente, U.idamico, Livello + 1 FROM dbo.Utenti AS U JOIN Amici AS A ON A.idamico = U.idutente ) SELECT * FROM Amici WHERE /* Stabilisco l'ultimo livello di amicizia ** che devo considerare */ idamico <= @idamico /* Verifico che esista una relazione di amicizia */ AND EXISTS ( SELECT * FROM Amici WHERE idamico = @idamico ); GO /* Esempi */ EXEC dbo.GetAmicizie 1, 9; /* Output: idutente idamico Livello ----------- ----------- ----------- 1 2 0 2 4 1 4 7 2 7 9 3 (4 row(s) affected) */ EXEC dbo.GetAmicizie 1, 7; /* Output: idutente idamico Livello ----------- ----------- ----------- 1 2 0 2 4 1 4 7 2 (3 row(s) affected) */ EXEC dbo.GetAmicizie 4, 9; /* Output: idutente idamico Livello ----------- ----------- ----------- 4 7 0 7 9 1 (2 row(s) affected) */ EXEC dbo.GetAmicizie 4, 10; /* Output: idutente idamico Livello ----------- ----------- ----------- (0 row(s) affected) */ EXEC dbo.GetAmicizie 1, 5; /* Output: idutente idamico Livello ----------- ----------- ----------- (0 row(s) affected) */ /* Pulizia */ DROP PROCEDURE dbo.GetAmicizie; DROP TABLE dbo.Utenti;

In caso di parentela otterrai le righe interessate, altrimenti otterrai un result set vuoto.
Per approfondire il discorso CTE leggi il seguente paragrafo sui Books Online:

"WITH common_table_expression (Transact-SQL)"
http://msdn2.microsoft.com/en-us/ms175972.aspx

Ciao!
--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

corkie 12 Profilo | Newbie

ma funziona anche con sql server 2000??????

lbenaglia Profilo | Guru

>ma funziona anche con sql server 2000??????
No, le CTE sono state introdotte a partire dalla versione 2005.

Ciao!
--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

corkie 12 Profilo | Newbie

e come faccio se uso quel dannato sql 2000...nn c'è un rimedio????
la tua soluzione era stupenda ma non riesco a farla girare in quanto uso il 2000
che tristezza.. n c'è un metodo per la quale io riesca a farlo girare su 2000.??

lbenaglia Profilo | Guru

>e come faccio se uso quel dannato sql 2000...nn c'è un rimedio????
>la tua soluzione era stupenda ma non riesco a farla girare in
>quanto uso il 2000
>che tristezza.. n c'è un metodo per la quale io riesca
>a farlo girare su 2000.??

Il modo c'è e consiste nell'emulare la CTE ricorsiva che ho scritto nel precedente post utilizzando ad esempio una user-defined function table level:

USE tempdb; CREATE TABLE dbo.Utenti( idutente int NOT NULL PRIMARY KEY, idamico int NOT NULL ); INSERT dbo.Utenti VALUES(1, 2); INSERT dbo.Utenti VALUES(3, 5); INSERT dbo.Utenti VALUES(2, 4); INSERT dbo.Utenti VALUES(4, 7); INSERT dbo.Utenti VALUES(8, 10); INSERT dbo.Utenti VALUES(6, 3); INSERT dbo.Utenti VALUES(7, 9); GO CREATE FUNCTION dbo.ufn_Amici( @idutente int ) RETURNS @tree TABLE( idutente int NOT NULL PRIMARY KEY, idamico int NOT NULL, Livello int NOT NULL ) AS BEGIN DECLARE @lvl AS int SET @lvl = 0 /* Anchor member */ INSERT @tree SELECT idutente, idamico, @lvl FROM dbo.Utenti WHERE idutente = @idutente /* Recursive member */ WHILE @@ROWCOUNT > 0 BEGIN SET @lvl = @lvl + 1 INSERT @tree SELECT U.idutente, U.idamico, @lvl FROM dbo.Utenti AS U JOIN @tree AS T ON U.idutente = T.idamico AND T.Livello = @lvl - 1 END RETURN END; GO CREATE PROCEDURE dbo.GetAmicizie( @idutente int, @idamico int ) AS SELECT * FROM dbo.ufn_Amici(@idutente) WHERE /* Stabilisco l'ultimo livello di amicizia ** che devo considerare */ idamico <= @idamico /* Verifico che esista una relazione di amicizia */ AND EXISTS ( SELECT * FROM dbo.ufn_Amici(@idutente) WHERE idamico = @idamico ); GO /* Esempi */ EXEC dbo.GetAmicizie 1, 9; /* Output: idutente idamico Livello ----------- ----------- ----------- 1 2 0 2 4 1 4 7 2 7 9 3 (4 row(s) affected) */ EXEC dbo.GetAmicizie 1, 7; /* Output: idutente idamico Livello ----------- ----------- ----------- 1 2 0 2 4 1 4 7 2 (3 row(s) affected) */ EXEC dbo.GetAmicizie 4, 9; /* Output: idutente idamico Livello ----------- ----------- ----------- 4 7 0 7 9 1 (2 row(s) affected) */ EXEC dbo.GetAmicizie 4, 10; /* Output: idutente idamico Livello ----------- ----------- ----------- (0 row(s) affected) */ EXEC dbo.GetAmicizie 1, 5; /* Output: idutente idamico Livello ----------- ----------- ----------- (0 row(s) affected) */ /* Pulizia */ DROP PROCEDURE dbo.GetAmicizie; DROP FUNCTION dbo.ufn_Amici; DROP TABLE dbo.Utenti;

Ciao!
--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

corkie 12 Profilo | Newbie

grazie mille lorenzo!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Ti pongo un'ultima domanda dato che la tua soluzione è super f***.
Ma se io dovessi utilizzarla su grafi invece che su alberi cioè con idutente non primary key cosa dovrei fare????
nel senso:
idutente idamico
1 2
1 4
2 4
2 7
etc....
posso usare una struttura simile o mi cambia tutto???l'ultima cosa poi nn ti rompo più...

lbenaglia Profilo | Guru

>Ma se io dovessi utilizzarla su grafi invece che su alberi cioè
>con idutente non primary key cosa dovrei fare????

Boh, inserisci qualche idutente duplicato e guarda se il risultato che ottieni di piace
Se aggiungi la riga INSERT dbo.Utenti VALUES(2, 8); ai precedenti dati, avendo cura di eliminare il constraint PRIMARY KEY dalla tabella base e dalla tabella restituita dalla funzione, specificando 1 e 9 otterrai il seguente risultato:

/* Esempi */ EXEC dbo.GetAmicizie 1, 9; /* Output: idutente idamico Livello ----------- ----------- ----------- 1 2 0 2 4 1 2 8 1 4 7 2 7 9 3 (5 row(s) affected) */

Come vedi l'elaborazione non è proseguita anche per il secondo ramo.
Se vuoi percorrere tutti i rami ti suggerisco di contattare un consulente in gamba che ti sviluppi la soluzione

Ciao!
--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

corkie 12 Profilo | Newbie

Grazie mille lorenzo per le dritte e sopratutto per la pazienza.......ti ringrazio a ricorsione infinita...
Siamo a 2 birre che ti devo...
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2024
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5