Newsgroup: it.comp.www.php
Titolo: Join su 19 tabelle

Ebbene si, devo fare una join su 19 tabelle.
(personalmente non ho trovato altre soluzioni)
Poichï¿œ non vorrei demolire il server (anche se ï¿œ di test)
sapete dirmi se una join di questo tipo:
FROM tabella 1
      INNER JOIN tabella1.campo = tabella2.campo
      INNER JOIN tabella1.campo = tabella3.campo
      INNER JOIN tabella1.campo = tabelle4.campo
mi prende tutti i dati, di tutte le tabelle,
unendo tabella1.campo alle altre 4 tabelle o se
ï¿œ ricorsiva, nel senso che unisce tabella1 a
tabella2, poi tabella2 a tabella3, poi tabella3
a tabella4 e cosï¿œ via?
E' una operazione che non ho mai capito come
debba essere interpretata.
-- 
Non tocca a noi dominare tutte le maree del mondo,
il nostro compito ï¿œ di fare il possibile per la
salvezza degli anni nei quali viviamo,
sradicando il male dai campi che conosciamo.

(personalmente non ho trovato altre soluzioni)
Quanta roba c'e' nelle altre tabelle e a cosa serve?
E' il tipico caso in cui hai una tabella "master" in cui c'e' un
codice, e poi questo e' una chiave in un'altra tabella?
Nel caso, (a) il server non dovrebbe patire molto, se gli indici
sono messi amm�do, e (b) se la cardinalit� delle tabelle di look
aside e' contenuta, si puo' risolvere alla macellaia in PHP :-)
Leonardo
-- 
The cock doth craw, the day doth daw,
the channerin' worm doth chide:
gin we be mist oot o' oor place,
a sair pain we maun bide.

 aside e' contenuta, si puo' risolvere alla macellaia in PHP :-)
Sono degli archivi.
Ad esempio.
Utente 1 acquista determinate cose.
Terminato l'acquisto, i singoli record di queste determinate
cose vengono "clonati" (insert into ordine select prodotto.* where...)
in una tabella "ordine" (o meglio, in 19 tabelle, perch� sono 19
tipologie di prodotti diversi che vanno a comporre un singolo ordine.
Questo serve perch� se l'amministratore del sito dovesse 'modificare'
o cancellare un prodotto gi� venduto, l'ordine continuerebbe a mostrare
il prodotto cos� come era quando � stato comprato.
Ho bisogno, per�, di fare un report, e per farlo devo poter ricomporre
un ordine facendo le 19 join.
In pratica:
ordine
   + ---- prodotto
              +----- prodotto_configurazione
   + ---- servizi
   + ---- blablabla
Creo l'ordine (che � un riepilogo, sostanzialmente)
Prendo l'id ordine.
Clono il prodotto e gli associo l'id ordine
Prendo l'id del prodotto appena clonato
Clono la configurazione del prodotto e gli associo
l'id prodotto appena clonato, in modo da fare la
gerarchia.
Un macello insomma, ma non ho idee migliori.
Una soluzione, alla quale sto pensando, � quella di
creare una piccola join tra le tabelle principali
(odine-prodotto-servizi-blablabla) senza entrare
nel dettaglio di ciascun elemento facente parte
dell'ordine (quindi niente configurazione).
Cos� facendo, posso limitare la query a 5 tabelle.
Quando apro il dettaglio del prodotto, prendo i dati
direttamente dalle singole tabelle con una
" SELECT * FROM xxx WHERE id IN (..)"
O no?
-- 
Non tocca a noi dominare tutte le maree del mondo,
il nostro compito � di fare il possibile per la


il prodotto cos� come era quando � stato comprato.
Kaiiiii. C'erano un paio di sistemi migliori di fare le cose :-(
Quindi cio' significa che nella tabella t5 potresti avere tante righe
quante sono gli ordini fatti di roba che doveva andare in t5, e quasi
tutti i record saranno duplicati (anzi se il prodotto non � mai stato
aggiornato, saranno davvero tutti duplicati).
Quanti aggiornamenti ha, tipicamente, un prodotto?
Potresti implodere ogni tabella in una tabella LR temporanea:
	SELECT  MIN(product_id) AS pid_from,
		MAX(product_id) AS pid_to,
		...tutti i campi della tabella al limite nella forma
			MIN(cippalippa) AS cippalippa
	FROM tabella12
		GROUP BY ...tutti i campi eccetto product_id...
		ORDER BY pid_from;
La tabella temp_tabella12 avra' una cardinalita' molto minore (forse
addirittura una sola riga) e puoi sostituire
	JOIN tabella12 ON (campomaster = tabella12.product_id)
con
	JOIN temp_tabella12 ON (campomaster >= temp_tabella12.pid_from
		AND campomaster <= temp_tabella12)
L'indice UNIQUE ON pid_from, pid_to dovrebbe consentire prestazioni di
parecchio superiori, e l'accorpamento delle tabelle potresti farlo NON
temporaneo ma a intervalli.
Ti rimangono le 19 join, ma diventano un bel po' piu' rapide. Se tutto
l'ambaradan non ha subito troppe modifiche, al 99% ti sta tutto quanto
nella cache.
===
Stessa cosa si puo' fare _SENZA_ join caricando tutto in PHP usando 19
array. Ciascuno lo carichi con una SELECT come quella temporanea vista
sopra. Poi fai una sola SELECT sulla tabella master.
A quel punto ti ritrovi dei campi che non c'entrano niente, es.
	$tuple['product']	=> 19,
	$tuple['servizio']	=> 227,
Adesso basta tradurre tupla per tupla a spese della CPU dov'e' il PHP:
	$tuple = array_map('join19',
		array_keys($tuple),
		array_values($tuple),
		'product_name');
	con
	function join19($field, $key, $translate_to)
	{
		GLOBAL	$_TLB;
		$tlb = $_TLB[$field];
		// Ora si cerca, fra le chiavi di $tlb, il massimo
		// dei minoranti di $value.
		// Per esempio se ho:
		// 13 => 'Prodotto di settembre'
		// 15 => 'Prodotto di ottobre'
		// 25 => 'Prodotto di novembre'
		// e ho il 21, appena vedo il 25 capisco che ho per
		// le mani un prodotto venduto a ottobre.
		$res	= false; // ERROR
		foreach($tlb as $index => $row)
		{
			if ($index > $value)
				return $res;
			$res = $row[$translate_to];
		}
		return $res;
	}
$_TLB['prodotto'] e' l'array con
	'min(product_id)'	=> $tupla_del_prodotto
Leonardo (aka "il macellaio di Lamporecchio")


 aggiornato, saranno davvero tutti duplicati).
Forse non mi sono spiegato.
Ci riprovo.
Premessa: le tabelle master cambiano di frequente e per frequente
intendo anche 20 volte al giorno.
Supponiamo di avere un negozio di computer.
In una tabella ho le schede madri.
Ciascuna scheda madre pu� avere abbinati X componenti,
vendibili solo con quella scheda madre.
Ciascuna tipologia di componente avr� la sua tabella.
tab_schedemadri
tab_schedevideo
tab_cpu
tab_memoria
tab_schedemadri � la tabella master, con un suo bel ID autoincrement.
L'ID1 corrisponde sempre e solo ad una precisa scheda madre e cos� via.
tab_schedevideo avr� tutte le schede video disponibili in magazzino
associate a ciascuna scheda madre. Ad esempio, una GEFORCE 500 avr� ID 1 
(autoincrement) e sar� associata grazie ad un id_schedamadre pari ad 1.
Se la scheda madre 2 avr� sempre una geforce 500, io avr� un altro
record geforce 500. (nel mio caso, in realt�, non ci possono essere
doppioni. un 'item' � associato solo ad un record)
tutte le altre tabelle sono uguali. Tutte puntano alla tabella madre.
Quando un cliente compra, io devo salvare il suo ordine in delle tabelle
clone, in modo da avere sempre lo storico di cosa � stato preso ed in
quale configurazione, indipendentemente dalle modifiche fatte dal
gestionale.
ordine_tab_schedamadre
ordine_tab_schedevideo
creo il record in ordine_tab_schedamadre con alcuni dati generici
(importo totale ordine, numero di pezzi, cliente, etc etc).
poi clono la scheda video che � stata comprata nella tabella
ordine_tab_schedevideo sostituendo l'id che in precedenza corrispondeva
alla scheda madre di riferimento, con l'id della scheda madre ma
generato dalla tabella ordine.
In pratica replico i dati.
Non � un negozio di computer, la faccenda � molto, molto pi� incasinata.
Il problema della join � che mi genererebbe qualcosa tipo 240 campi, per
ciascun 'item' presente in fase di ordine, perche mi aggiungerebbe i
campi di ciascuna tabella al resultset e ciascun record corrisponderebbe
ad un item.
Se ci fossero 15 ordini nella giornata odierna, la query sparerebbe
fuori 15 righe, per 240 campi l'una.

ad un item.
Ma questo "sottoproblema" lo risolvi alterando la SELECT:
SELECT ordine.*, tab_schedevideo.descrizione, tab_cpu.descrizione,
...
in modo da prendere tutte le colonne di ORDINE, ma solo una fra le
colonne di ciascuna tabella di lookup. Anzi, meglio ancora:
SELECT 
ordine.id
, ordine.importo, ordine....,
	tab_schedevideo.descrizione, ...
(Questo sistema ha anche un altro problema - e se uno prende un PC
senza DVD-ROM? O senza scheda video esterna? La join ci mette NULL
e va avanti?
Forse converrebbe studiare meglio il report, e al limite generarlo
facendo una serie di query per ogni ordine. Piu' pesante, ma molto
meno complesso concettualmente.
Leonardo IHVVHO



mi sa che hai un p� di confusione sulle basi di dati.
NON SI REPLICANO MAI I DATI.
vediamo se riesco a darti una mano o se ho capito.
- tab_schedemadri
- tab_schedevideo
- tab_cpu
- tab_memoria
in primis non avrei mai organizzato il db cos�
per diversi motivi.
il primo molti campi di queste tabelle sono comuni.
secondo se per caso esce un nuovo tipo di hardware che fai crei una 
nuova tabella ??? (ti ricordo per esempio che fino a pochi anni fa i 
lettori mp3, o i gadget usb non esistevano).
comunque avendo queste tabelle proviamo ad andare avanti.
mi sorge una domanda che ci fai con l'ID autoincrement che da quel che 
vedo ti serve come codice univoco(immagino chiave primaria)?? non basta 
il codice stesso della scheda madre (che di per se � gia univoco per 
ogni marca e modello)?? facendo come ti suggerisco hai il vantaggio di 
creare una chiave primaria.
se fai come hai fatto oltre ad avere una chiave primaria che non serve a 
nulla (a che serve un autoincrement in questo caso), poi sarai costretto 
ad indicizzare anche il codice della scheda madre.
la seconda parte che hai scritto quella delle schede video associate 
spero non ti abbia letto un qualsiasi DBA.
allora vediamo come dovrebbe essere organizzato.
tab_schedemadri -> cod_scheda_madre (e relativi campi)
tab_schedevideo -> cod_scheda_video (e relativi campi)
adesso una bellissima tabella di raccordo n:n (detta enne a enne)
tab_componenti
--------------
cod_scheda_madre
cod_scheda_video
per farti un esempio :
tab_componenti
skmadre - skvideo
    1    -  1
    1    -  2
    2    -  2
    2    -  3
    3    -  1
in questo modo non replichi nessun dato, ma metti in relazioni le schede 
madri con le schede video.
se vuoi sapere a quali schede madri appartiene una certa scheda video fai:
select a.cod_scheda_madre
      , a.descrizione
   from tab_schedemadri a,
        tab_componenti b
   where b.cod_scheda_video = [tuo codice scheda]
     and a.cod_scheda_madre = b.cod_scheda_madre;
se vuoi sapere quali schede video appartengono ad una scheda madre fai:
select a.cod_scheda_video
      , a.descrizione
   from tab_schedevideo a,
        tab_componenti b
   where a.cod_scheda_madre = [tuo codice scheda]
     and a.cod_scheda_video = b.cod_scheda_video;
gli ordini di un cliente sono master/detail
tab_ordine
-----------
numero_ordine (univoco)
cod_cliente
data
ecc..
ecc..
dett_ordine
--------------
numero_ordine (non univoco fa riferimento alla tab_ordine)
cod_articolo
prezzo
ecc..
nel tuo caso possiamo avere :
dett_ordine
------------
numero_ordine (non univoco fa riferimento alla tab_ordine)
tipo_articolo (una codifica che ti dice se scheda madre, scheda video, ecc.)
cod_articolo
prezzo
iva
sconto
ecc..
se per caso il prezzo deve essere sulle vendite e non sui prodotti (come 
giusto che sia) devi creare una tabella che ti indica il prezzo di 
acquisto, questo per spiegarti che i prezzi di acquisto e vendita di un 
bene variano nel tempo, ma qui entriamo nella gestione di magazzino che 
� ben complicata e ci vorrebbe un libro solo per spiegarti come funziona.
per farti un esempio banale
oggi compro 10 pezzi della skvideo 1 a 10 euro
domani compro 28 pezzi della skvideo 1 a 8 euro
fra 3 gg compro 7 pezzi della skvideo 1 a 12 euro
vendo le stesse ad un prezzo differente in un periodo + o - lungo
tab_acquisto
------------
data_acquisto
cod_fornitore
tipo_articolo (codifica che ti dice se scheda madre, scheda video, ecc.)
cod_articolo
pezzi
prezzo
iva
totale
ecc.
tab_prezzi_vendita
------------------
data_da
data_a
tipo_articolo (codifica che ti dice se scheda madre, scheda video, ecc.)
cod_articolo
prezzo
tipo_iva
oppure la scelta di come organizzare un magazzino FIFO(first in first 
out) LIFO (last in first out),  ecc..
NB.
le tabelle che fanno riferimento hai componenti (schede video, madri, 
cpu ecc.) i dati NON devono essere variabili come caratteristiche se c'� 
bisogno di variarle si inserisce un nuovo record, a meno di errori di 
inserimento.
per farti un esempio
se una scheda madre ha un certo tipo di caratteristica e successivamente 
il produttore ne cambia le caratteristiche, bisogna aggiungere un nuovo 
record con le nuove caratteristiche, altrimenti si rischia di avere dei 
dati non congrui con eventuali vendite effettuate precedentemente.
Se hai dubbi non esitare.
Ciao

 	1	3
Io credo di non riuscire a spiegarmi.
Purtroppo non posso entrare nel dettaglio del progetto,
ma la situazione � molto, molto pi� complessa.
Non sono prodotti hardware, ma 'servizi' (diciamo cos�).
La tabella di relazione da te indicata, non la posso
usare. Solitamente faccio le relazioni proprio come hai
indicato te, ma in questo caso, non � possibile.
Sto valutando anche l'esecuzione di singole query, come dici
te � pi� pesante, ma almeno non divento cretino a sviluppare
il codice per ritrovarmi con 250 colonne.

 creare una chiave primaria.
No, perch� non avendo alcuna scheda madre, non ho alcun codice scheda.
Rileggi le mie mail, per cortesia.

Non sono prodotti hardware, ma 'servizi' (diciamo cos�).
Ahhh. Hum. Ecco perche' dici che non hai "duplicati in archivio".
...ho paura che, salvo esami specifici del database, dovrei passare
una sentenza di "Una query master e tante query secondarie".
Non il massimo, ammetto :-(
Leonardo


 Non il massimo, ammetto :-(
Per ora faccio UNA query: prendo l'elenco degli ordini e basta.
Nella tabella ordini ci sono tutti i dati riepilogativi, come data
ora, importo, cliente, per cui nel 99% dei casi basta ed avanza per
fare ogni tipo di statistica. Quando poi si va ad aprire il dettaglio
ordine, cliccando su un ordine ben preciso, posso anche pensare di
fare le 19 query singole o al massimo di fare delle join a gruppi
di 4-5 query, evitando di farne una unica con 19 tabelle.
Per come son strutturate le tabelle archivio, dovrei riuscire a
realizzare 4 join da 4-5 tabelle l'una.
Ma tornando in-topic, la mia domanda era un altra. :)
Se faccio delle join tipo
FROM tabella1
      LEFT JOIN tabella2 ON tabella1.campo = tabella2.campo
      LEFT JOIN tabella3 ON tabella1.campo = tabella3.campo
e cos� via, la seconda,terza,quarta,... join � riferita sempre
alla tabella1, o mi fa la join della join subito prima?
La domanda � 'strana' lo ammetto, ma in passato ho ricevuto errori
tipo colonne non trovate che ho risolto semplicemente spostando
l'ordine delle join. E confesso di non aver capito bene che ragionamenti
faccia mysql.

 wrote:
[cuttone]
allora può essere che mi sia sfuggito qualcosa,
per servizi cosa intendi???
cioè per farti un esempio
un concessionario oltre a vendere l'auto offre una serie di servizi
come assistenza, sostituzione auto, officina, polizze assicurative
ecc..
può calzare come esempio???
se si
potremmo procedere con diversi modi, fammi sapere
Ciao

 come assistenza, sostituzione auto, officina, polizze assicurative
Un costruttore di automobili compone delle auto.
Crea una auto.
A quella singola auto, associa una gomma, un vetro,
una marmitta, un volante.
Per cui, io ho una tabella con le gomme, una tabella
con i vetri, una tabella con le marmitte.
Questo perch� nessuno dei campi in questione � in comune.
I campi della tabella gomme sono *tutti* differenti da
quelli della tabella volante.
Se cos� non fosse, avrei fatto una singola tabella, con un
id o una chiave che ne identifica la tipologia (volante|gomma)...
Ma sono campi tutti diversi, non compatibili tra loro, per cui
sono tabelle tutte diverse.
Il mio problema � pescare, partendo dalla tabella principale
con l'auto, tutti i componenti inseriti al suo interno.
E devo joinare 19 tabelle.



prendendo questo esempio
 si devi fare una join su tutte le tabelle, a meno che riesci a
normalizzare il db.
una altra soluzione che secondo me è la più pulita è quella di creare
una vista che fa esattamente le 19 join, e la tua applicazione fa una
"select * from [vista]"
ricorda che una vista può essere moooooolto più veloce di una select
oppure può avere la stessa velocità, molto dipende da come è
strutturato il db e quante operazioni di scritture vengono eseguite
sulle vere tabelle.
in genere la view si popola con la prima query che gli viene richiesta
e tale rimane (cache), se vengono effettuate operazioni di scrittura
sulle vere tabella allora il successivo accesso alla view ripopola la
stessa (e ri-cahe), altrimenti se non ci sono operazioni di scrittura
sulle tabelle la tua query sulla view non ripopola la view, ma prende
direttamente dalla cache, queste si chiamano Materialized View
NB
questo sopra descritto non si applica a Mysql che attualmente non
gestisce le materialized view.
un altra soluzione (sporca) potrebbe essere quella di creare una
tabella con i campi che a te servono e popolarla tramite trigger dalle
tabelle.
quest'ultima soluzione è sporca perchè replica i dati.
Ciao

 diverse.
Sbagli candeggio. :)
Fai una tabella master "prodotti" con 2 campi: id e tipo
E poi fai le 19 tabelle secondarie con id e le varie caratteristiche.
Poi fai (fino a ) 20 query. La prima sara`
select * from prodotti order by tipo, id;
Quindi fai un ciclo in PHP che prende i gruppi di prodotti (per tipo) e 
genera le altre 19 query come
select * from tabella_tipo where id in (lista degli id)
PostgreSQL supporta anche una sorta di ereditarieta` per questo, ma non 
e` semplice da sfruttare in PHP.
Fare una join tra tutte e 19 le tabelle implica che le colonne di tali 
tabelle siano analoghe tra di loro, quindi, se lo fossero, tanto varrebbe 
fare una macrotabella con una colonna "tipo" e n colonne "dato_1", 
"dato_2", ... "dato_n" e poi distinguere lato codice a cosa corrisponde 
"dato_25" per il "tipo_telaio".
Spero di essermi spiegato. :)
Bye.

l'ordine delle join.
Errori di colonne non trovate non dovresti averne mai. Se hai
usato sempre lo specificatore di tabella, un errore cosi' non
puo' che venire da un bug dell'algoritmo che calcola il query
plan.
L'errore, invece, si manifesta spesso, se lasci per strada lo
specificatore di tabella. Se la terza join unisce un campo da
tabella4 a tabella2, puo' succedere (in PostgreSQL no, ma con
MySQL magari si') che la quarta join trovi un campo in join e
lo possa usare dalla tabella4, mentre, se scambi le join, non
lo trova piu'.
Ed in altri casi, con due campi con lo stesso nome in tabelle
diverse, potresti avere risultati inattesi (in realta', credo
che comunque dovresti ottenere un warning, se non un errore e
l'arresto della query, per ambiguita' nella specifica. Ma non
vorrei sperimentarlo di persona :-) ).


 "select * from [vista]"
Impossibile, perch� io non devo prendere ogni volta tutti i dati di
tutti gli ordini. Anzi, non capiter� mai che debba prendere i dati
di tutti gli ordini con annesse le 19 tabelle.
O no?

 select * from tabella_tipo where id in (lista degli id)
Minkia!
Volevo evitare 20 query....
Stavo facendo una join proprio per non fare 20 query, altrimenti il
problema non sussisterebbe.

 lo trova piu'.
Grazie del chiarimento


la select sar� select * from [vista] where id_auto = [tuo codice]
diciamo che la soluzione della vista ti evita la rottura di scatole di 
mettere mano al codice dell'applicazione e relativo sql se un domani 
dovessero combiare qualcosa all'interno della struttura del db, in quel 
caso hai un solo posto dove andare a modificare, ed � la vista.
Ciao


select * from vista � generale, puoi fare una query come ti pare solo 
prendere i dati da un unica tabella (la vista)
per esempio select [campo 1],[campo 2],[campo n] from [vista] where 
cod_auto = [tuo codice] and [bla] = [bla]
order by [bla] ecc..
Ciao

 la select sar� select * from [vista] where id_auto = [tuo codice]
si si, ignora pure la mia domanda, mi stavo facendo
delle paranoie inutili.

 debba essere interpretata.
Mi sono venute in mente un paio di idee che vi vorrei sottoporre:
a) UNION delle 19 tabelle
select "A" as tipo_oggetto, concat( field_a1,"#",field_a2 ) as dati_oggetto
FROM table_a
WHERE table_a.id_ordine = XYZ
UNION
select "B" as tipo_oggetto, concat( field_b1,"#",field_b2 ) as dati_oggetto
FROM table_b
WHERE table_b.id_ordine = XYZ
select "C" as tipo_oggetto, concat( field_c1,"#",field_c2 ) as dati_oggetto
FROM table_c
WHERE table_c.id_ordine = XYZ
in questo modo sai esattamente da quale tabella proviene il record (cosa 
che nella join ti devi smazzare per benino) l'unico problema ï¿œ 
spacchettare i dati che ti ritorna in dati_oggetto.
---
Non  mi ï¿œ chiaro quali tipi di ricerche devi fare su questi dati 
storici: hai parlato solo di un report a partire dall'ordine ma non hai 
detto se c'ï¿œ la necessitï¿œ di sapere a partire da un "codice prodotto" 
quali ordini lo contengono... immagino che questa esigenza non ci sia 
perchï¿œ in ogni caso hai detto che al prodotto cambi ID... quindi:
b)campo TEXT
metti un campo di tipo TEXT nella tabella ordini (o meglio, in altra 
tabella con join 1:1) dove memorizzerai in formato XML/JSON o php 
serialize o come vuoi tu, i dati dell'ordine. In questo modo avresti 
comunque la possibilitï¿œ di fare delle query con la LIKE.
c) usa un db esterno
tipo mongodb, tra l'altro supportato benissimo in php e molto veloce, 
dove ogni record puï¿œ avere campi differenti dall'altro ! In realtï¿œ non 
possiamo parlare di record ma chiamiamoli "oggetti JSON"
Francesco