161 messaggi dal 07 settembre 2009
Salve a tutti =)
ho 4 tabelle indipendenti tra loro che si relazionano ad una quinta tabella, e avrei bisogno di relazionarle insieme contemporaneamente.
Il problema è che ho sempre bisogno dei dati della quinta colonna a cui si relazionano le altre anche se la relazione è falsa. Per fare ciò il LEFT OUTER JOIN sarebbe perfetto, solo che relazionando più tabelle, dato che ognuna potrebbe restituire più di un record, ho come risultato che i valori si ripetano più volte.

Es. date le tabelle t1,t2,t3,t4,ta (dove ta è la tabella a cui si relazionano le altre 4) ottengo i seguenti risultati

ta.valore1 t1.a1 t2.b1 t3.c1 t4.d1
ta.valore1 t1.a1 t2.b1 t3.c1 t4.d2
ta.valore1 t1.a1 t2.b1 t3.c1 t4.d3
ta.valore1 t1.a1 t2.b1 t3.c2 t4.d1
ta.valore1 t1.a1 t2.b1 t3.c2 t4.d2
ta.valore1 t1.a1 t2.b1 t3.c2 t4.d3
ta.valore1 t1.a1 t2.b2 t3.c1 t4.d1
ta.valore1 t1.a1 t2.b2 t3.c1 t4.d2
ta.valore1 t1.a1 t2.b2 t3.c1 t4.d3
ta.valore1 t1.a1 t2.b2 t3.c2 t4.d1
ta.valore1 t1.a1 t2.b2 t3.c2 t4.d2

ecc ecc...

Considerando che stò lavorando con asp.net (c#), il fatto di avere la colonna di ta.valore1 che si ripeta più volte non rappresenta un problema, perchè con un if controllo se lavorare con quella variabile o meno... cosa che però non posso fare con gli altri campi...

Come fare? Che devo inventarmi per risolvere la situazione? Esiste qualche comando sql che mi eviti questo tipo di ripetizione? O mi conviene concentrarmi sull'asp.net e usare tanti if? Avevo anche pensato all'utilizzo di tabelle nidificate, ma cercando sul web non ho trovato degli esempi concreti accompagnati da qualche riga di codice.


Come sempre grazie per l'aiuto

Davide
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,

doppiomango ha scritto:

Esiste qualche comando sql che mi eviti questo tipo di ripetizione?


beh, sì, potresti raggruppare quei risultati con una clausola GROUP BY.

Hai postato l'elenco dei record che ottieni ora, ma posta anche quello che invece vorresti ottenere così è più facile darti un consiglio.

ciao

Enjoy learning and just keep making
161 messaggi dal 07 settembre 2009
Grazie per l'aiuto.
Quello che vorrei ottenere è

ta.valore1 t1.a1 t2.b1 t3.c1 t4.d1
ta.valore1 t1.a2 t2.b2 t3.c2 t4.d2
ta.valore1 null t2.b3 null t4.d3
ta.valore1 null null null t4.d4
ta.valore2 t1.a3 t2.b4 null null
ta.valore2 t1.a4 t2.b5 null null

per farla breve il campo della tabella ta si ripete tante volte quant'è il numero massimo di campi delle altre 4 tabelle relazionate, ovvero se al valore di ta.valore1 la tabella t1 restituisce 2 valori, t2 3 valori, t3 2 valori e t4 4 valori allora ho 4 ripetizioni del campo ta.valore1, e nelle righe in cui le tabelle t1,t2 e t3 non sono presenti valori ho null... tenendo presente che non so quale delle 4 tabelle ha più campi...
per adesso ho risolto con codice asp.net ma è una soluzione pellegrina, perchè devo leggere le tabelle un numero spropositato di volte.

La soluzione più "semplice" sarebbe quella di fare la select sulla prima tabella e nel while fare altre 4 select distinte in cui faccio le outer join con le 4 tabelle singolarmente... ma è una soluzione che mi fa venire la pelle d'oca...


Davide
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,

doppiomango ha scritto:

per adesso ho risolto con codice asp.net ma è una soluzione pellegrina, perchè devo leggere le tabelle un numero spropositato di volte.


Sì, effettivamente non è la soluzione migliore, sarebbe meglio delegare al database il compito di trovare un modo per restituirti quelle righe.

Ti linko una sessione di Davide Mauri che spiega un altro approccio di affrontare questo genere di problemi senza usare cicli o codice .NET. Dagli un'occhiata se hai tempo, è veramente interessante, ti può dare degli spunti utili.
Video: T-SQL Set-based thinking

Penso di aver capito ciò che vuoi ottenere, ma quei null che hai inserito tra i risultati NON salteranno fuori con delle JOIN "semplici".
Devi predisporre una funzione o tabella che ti restituisca dei numeri, come vedi al minuto 27:30, e poi farne la join (cross join, per l'esattezza) con ta. In quella sessione si parla di Sql Server, ma se tu usi un altro DBMS il concetto resta il medesimo. Il risultato che otterrai sarà una roba del genere:

rownum, ta.valore
1, valore1
2, valore1
3, valore1
4, valore1
1, valore2
2, valore2
3, valore2
4, valore2
In pratica ti prepari ad accogliere fino a 4 valori provenienti dalle altre tabelle t1~t4 per ciascun valore di ta.

Allo stesso modo, anche i record di t1~t4 dovranno essere numerati affinché tu possa sapere quale di quelli è il primo, il secondo, il terzo, ecc... per ciascun valore di ta (quindi in t1 il conto deve ricominciare ogni volta che cambia il valore della foreign key che lega t1 a ta).
Questo, se usi Sql Server, lo ottieni grazie alla funzione ROW_NUMBER, ne vedi un'utilizzo al minuto 40:30.

SELECT t1.taID, t1.valore, ROW_NUMBER() OVER (PARTITION BY t1.taID ORDER BY t1.taID) as numeroRiga FROM t1
Invece, se usi MySql, credo che potresti utilizzare un sistema simile a questo.

Ora che hai questi elementi, prova a combinarli così. Qui faccio la join solo con t1 e t2 per non allungare esageratamente la query.
WITH tabella1 AS (
SELECT t1.taID, t1.valore, ROW_NUMBER() OVER (PARTITION BY t1.taID ORDER BY t1.taID) as numeroRiga FROM t1
),
tabella2 AS (
SELECT t2.taID, t2.valore, ROW_NUMBER() OVER (PARTITION BY t2.taID ORDER BY t2.taID) as numeroRiga FROM t2
)
SELECT indici.indice, ta.Valore, tabella1.Valore, tabella2.Valore
FROM indici CROSS JOIN ta
LEFT JOIN tabella1 ON (ta.ID = tabella1.taID AND indici.indice=tabella1.numeroRiga)
LEFT JOIN tabella2 ON (ta.ID = tabella2.taID AND indici.indice=tabella2.numeroRiga)
ORDER BY ta.ID, indici.indice;
Questo produce, come da te richiesto, un risultato del genere:

1  ta.valore1     t1.a1   t2.b1        
2  ta.valore1     t1.a2   t2.b2        
3  ta.valore1     t1.a3   NULL
4  ta.valore1     NULL    NULL
1  ta.valore2     t1.a4   t2.b3        
2  ta.valore2     NULL    t2.b4        
3  ta.valore2     NULL    t2.b5        
4  ta.valore2     NULL    NULL
Puoi escludere i risultati che contengono solo dei null imponendo questa condizione (o qualcosa di simile) nella clausola WHERE.
WHERE COALESCE(tabella1.Valore, tabella2.Valore) IS NOT NULL

Il problema di tutto questo sistema è che devi conoscere a priori il numero massimo di record che possono arrivare da t1~t4 per ciascun valore di ta. Qui io ho ipotizzato che fossero 4, ma magari tu ne hai un numero di variabile che non saprei come calcolarmi dinamicamente.
Poi, il piano di esecuzione di questa query non mi pare granché, c'è sicuramente margine di miglioramento.

Magari Andrea Montanari, MVP per SQL Server, se legge questo thread può suggerirti un sistema più efficiente.

ciao
Modificato da BrightSoul il 20 dicembre 2011 22.22 -

Enjoy learning and just keep making
42 messaggi dal 13 settembre 2007
Ciao volevo mostrarti la mia soluzione che comunque è molto simile a quella postata in precedenza.
Ti posto l'esempio completo:
/*inizio riempimento variabili di test*/
declare  @ta table(id varchar(50))
declare  @t1 table(id varchar(50), ta_id varchar(50))
declare  @t2 table(id varchar(50), ta_id varchar(50))
declare  @t3 table(id varchar(50), ta_id varchar(50))
declare  @t4 table(id varchar(50), ta_id varchar(50))

insert into @ta values('valore1'),('valore2')
insert into @t1 values('a1','valore1'),('a2','valore1'),('a3','valore2'),('a4','valore2')
insert into @t2 values('b1','valore1'),('b2','valore1'),('b3','valore1'),('b4','valore2'),('b5','valore2')
insert into @t3 values('c1','valore1'),('c2','valore1')
insert into @t4 values('d1','valore1'),('d2','valore1'),('d3','valore1'),('d4','valore1')
/*fine riempimento variabili di test*/

/*step 1: per ogni tabella collegata recupero la posizione all'interno del gruppo determinato dalla chiave di @ta, la tua tabella comune*/
;with temp
as
(
  select id as id, 't1' as table_id, ta_id, row_number() over(partition by ta_id order by id) as row from  @t1 as t1
  union
  select id as id, 't2' as table_id, ta_id, row_number() over(partition by ta_id order by id) as row from  @t2 as t2
  union
  select id as id, 't3' as table_id, ta_id, row_number() over(partition by ta_id order by id) as row from  @t3 as t3
  union
  select id as id, 't4' as table_id, ta_id, row_number() over(partition by ta_id order by id) as row from  @t4 as t4
)

/*step 2: in primo luogo recupero per ogni chiave di @ta i valori distinct delle posizioni all'interno di questo gruppo... per ognuna delle tabelle da "joinare", recupero i valori con la posizione e il gruppo corrente*/
select row_numbers.ta_id
,row_numbers.row
,t1.id as t1_id
,t2.id as t2_id
,t3.id as t3_id
,t4.id as t4_id
from (select distinct ta_id, row  from temp) as row_numbers /*recupero per ogni chiave @ta i numeri di riga*/
left join temp as t1 on t1.ta_id = row_numbers.ta_id and t1.row = row_numbers.row and t1.table_id = 't1'
left join temp as t2 on t2.ta_id = row_numbers.ta_id and t2.row = row_numbers.row and t2.table_id = 't2'
left join temp as t3 on t3.ta_id = row_numbers.ta_id and t3.row = row_numbers.row and t3.table_id = 't3'
left join temp as t4 on t4.ta_id = row_numbers.ta_id and t4.row = row_numbers.row and t4.table_id = 't4'
order by row_numbers.ta_id,row_numbers.row


Il risultato di tutto questo è questo:
ta_id   |row|t1_id|t2_id|t3_id|t4_id
valore1 | 1 |  a1 |  b1 |  c1 |  d1
valore1 | 2 |  a2 |  b2 |  c2 |  d2
valore1 | 3 | NULL|  b3 | NULL|  d3
valore1 | 4 | NULL| NULL| NULL|  d4
valore2 | 1 |  a3 |  b4 | NULL| NULL
valore2 | 2 |  a4 |  b5 | NULL| NULL


Che è il risultato se non mi sbaglio da te voluto.
Il problema è che se hai altre tabelle da collegare, devi modificare entrambe le query. Oltretutto se ci sono molti record sinceramente non saprei dirti l'efficienza.
Per quanto riguarda la union potresti fare come nell'esempio precedente e creati n CTE.

Spero di esserti stato di aiuto.
Modificato da roland79 il 21 dicembre 2011 08.40 -
161 messaggi dal 07 settembre 2009
Grazie ad entrambi per il prezioso aiuto =)

Ho adottato la soluzione di roland79 (apportando le dovute modifiche), perchè non posso stabilire un numero massimo di record che ogni tabella può tirar fuori.
In ogni caso, grazie ad entrambi, ora conosco dei nuovi comandi sql che prima mi erano ignoti (over, partition by ed union) =)


Davide
Modificato da doppiomango il 21 dicembre 2011 10.50 -

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.