Mostra dati - evitare duplicazione - mysql_fetch_assoc()

Ciao a tutti,

mi sono affacciato da poco al mondo di php/sql e ho un problema nel mostrare dei dati in una pagina html.

Ho dato un'occhiata a vecchi posti del forum ma non sono riuscito a trovare una soluzione. spero che qualcuno possa aiutarmi.

Il problema consiste nel riuscire a scrivere in un'unica riga i diversi records restituiti da mysql_fetch_assoc(). Records relativi ad una sola entita' che pero' ha una relazione del tipo 1:M con un altra entita' (forse la terminologia non e' esatta, chiedo scusa....).

Dunque, ho un db di questo tipo:

[table]

[tr][td]FILM[/td][/tr]

[tr][td]id[/td][td]titolo[/td][td]anno[/td][/tr]

[tr][td]77[/td][td]Mulholland Drive[/td][td]2001[/td][/tr]

[/table]

[table]

[tr][td]GENERI[/td][/tr]

[tr][td]id[/td][td]descrizione[/td][/tr]

[tr][td]1[/td][td]Drammatico[/td][/tr]

[tr][td]2[/td][td]Thriller[/td][/tr]

[/table]

[table]

[tr][td]FILM_GENERI[/td][/tr]

[tr][td]film_id[/td][td]genere_id[/td][/tr]

[tr][td]77[/td][td]1[/td][/tr]

[tr][td]77[/td][td]2[/td][/tr]

[/table]

Ora,  faccio fare all'utente una query che cerchi tutti i film realizzati dal 2000 al 2008:

SELECT f.id, f.titolo, f.anno, g.descrizione as genere

FROM film as f

LEFT JOIN film_generi as fg

ON f.id = fg.film_id

LEFT JOIN generi as g

ON fg.genere_id = g.id

WHERE f.anno BETWEEN 2000 AND 2008

E scrivo i risultati in una tabella:

                     $res=mysql_query($query);
                                   
                    while ($row = mysql_fetch_assoc($res)){
                
                    echo "<tr>
                              <td>".$row[id]."</td>
                              <td>".$row[titolo]."</td>
                              <td>".$row[genere]."</td>
                              <td>".$row[anno]."</td>
                          </tr>";

                    }

In questo modo mi viene fuori qualcosa di simile a:

[table]

[tr][td]77[/td][td]Mulholland Drive[/td][td]Drammatico[/td][td]2001[/td][/tr]

[tr][td]77[/td][td]Mulholland Drive[/td][td]Thriller[/td][td]2001[/td][/tr]

[/table]

Come si puo' fare per ottenere i due record riuniti in una sola riga? Del tipo: 

[table]

[tr][td]77[/td][td]Mulholland Drive[/td][td]Drammatico,Thriller[/td][td]2001[/td][/tr]

[/table]

Ne ho provate di tutti i colori ma davvero non mi riesce....e temo di scoprire che non era davvero poi cosi' difficile...

confido nell'aiuto dei forumisti, grazie anticipatamente

salomone

inviato 8 anni fa
salomone
modificato 8 anni fa
X 0 X

Il problema della gestione delle relazioni 1 a molti è piuttosto comune.

Se vuoi risolverlo con una JOIN per prima cosa non puoi costruire la tabella HTML durante la fetch dei risultati. Nella fetch devi costruire un array di film. Ad ogni iterazione del ciclo while devi controllare se il film estratto dalla query è già presente in questo array. In caso negativo inserisci il film nell'array ed il genere in un altro array di generi, sempre all'interno del film. In caso affermativo eviti di inserirlo ma prelevi il genere del film e lo metti nell'arrya dei generi del film.

Alla fine del ciclo dovresto aver costruito un array del tipo:

$films = array(77 => array('titolo' => 'Mulholland Drive', 'anno' => 2001, 'generi' => array('Drammatico', 'Thriller')));

nota che per la chiave dell'array dei film ho usato l'id del film, in modo da poter facimente sapere se un film è stato già inserito.

 :bye:

risposto 8 anni fa
Gianni Tomasicchio
X 0 X

Grazie mille Gianni, ora provo a mettere in pratica quanto mi hai detto!

Mentre ci sono, quali sono le alternative all'uso della JOIN? forse fare un REFERENCE tra le tabelle del db? Capisco che e' un argomento forse troppo ampio per discuterlo in una thread, non e' che sapresti/e consigliarmi un articolo/sito che ne parli?

grazie ancora

Salomone

risposto 8 anni fa
salomone
X 0 X

L'alternativa al JOIN è effettuare una query per ogni record estratto. In generale questo approccio è inefficiente ma andrebbe considerato in alcuni casi, ad esempio una tabella conente dei file messa in JOIN con una tabella che ne specifica la categoria. I record in JOIN si moltiplicherebbero e quindi i Kb restituiti dalla query sarebbero troppi, ogni file sarebbe ripetuto per ciascuna categoria a cui appartiene.

 :bye:

risposto 8 anni fa
Gianni Tomasicchio
X 0 X

ciao a gianni e a tutti gli altri forumisti.

mi tocca ritornare sull'argomento che ho aperto perche' risolto un problema....ne nascono altri a catena. problemi che ho parzialmente risolto ma per i quali mi piacerebbe sapere se esistono alternative piu' "pulite" e professionali.

in generale, i problemi nascono dal fatto che l'entita' 'film' ha due relazioni 1:M e non solo una.

riporto di seguito la struttura del mio db e poi ti indico i miei dubbi.

FILM (id, titolo, anno)

GENERI (id, descrizione)

FILM_GENERI (film_id, generi_id)

LINGUE (id, descrizione)

FILM_LINGUE (film_id, lingue_id)

Come dicevo nel primo post, il mio obiettivo e' quello di mostrare i risultati agli utenti in una forma tipo:

77   Mulholland Drive    Drammatico,Thriller     Italiano, Inglese   2001

Vale la pena indicare che il form di selezione permette all'utente di eseguire diverse query, filtrando in base al titolo, a uno o piu' generi, a uno o piu' lingue, all'anno e dalle varie possibili combinazioni.

Prob 1

Una query che cerchi i film del 2001, per esempio, restituisce in $res= mysql_query_fetch()  i seguenti records (nella 1 riga ci sono gli alias che ho assegnato):

film_id     title                          year      genre             language         

-------------------------------------------------------------------------------

7       Mulholland drive      2001   Drammatico   Italiano

7       Mulholland drive      2001   Drammatico   Inglese   

7       Mulholland drive      2001   Thriller              Italiano

7       Mulholland drive      2001   Thriller              Inglese   

Per evitare che generi o lingue fossero scritti due volti nell'array  $films ho scritto questo codice:

while ($row = mysql_fetch_assoc($res)){                    
                    
                    if ($row[film_id] == $check )  {
                    
                                      if (!in_array ($row[genre],$film[$check][genre])){                                      
                                          $film[$row[film_id]][genre][] = $row[genre];                                          
                                          }
                                      
                                       if (!in_array ($row[language],$film[$check][language])){                    
                                          $film[$row[film_id]][language][] = $row[language];                                          
                                          }
                    }

                    else

                    $film[$row[film_id]] = array (
                                    'title' => $row[title],
                                    'year' => $row[year],
                                    'genre' => array ($row[genre]),
                                    'language' => array($row[language])
                                  );

                    $check = $row[film_id];

                                  
                          }

I dubbi piu' pressanti sono relativi a:

1) il controllo effettuato con IF e la funzione in_array

2)l'utilizzo di una nuova variabile $check per verificare se il film e' gia' stato inserito

esistono metodi migliori per eseguire le stesse operazioni?

Per evitare un post troppo lungo ne scrivo un altro in cui esporre il secondo problema....

risposto 8 anni fa
salomone
X 0 X

.....(continua)...

Prob 2

Se un utente vuole visualizzare tutti i film "Drammatici", come faccio a fare in modo che nell'output vengano mostrati anche gli altri generi attribuiti a tali film? (stessa cosa vale per la lingua)

Infatti, se l'utente seleziona "drammatico" nel form, i record che mettono in relazione il film con altri eventuali generi non verranno selezionati dalla query.

questo e' il mio tentativo. pare funzionare ma  mi piacerebbe sapere se esistono alternative migliori.

Ho proceduto  cosi':

// $query preleva solamente film_id in base alle selezioni dell'utente (es. film_id di tutti i film "drammatici")
$res=mysql_query($query);
// creo un array in cui andro' a scrivere tutti i film_id prelevati dalla $query
$film_id = array ();

//scrivo i valori nell'array
while ($row = mysql_fetch_assoc($res)){
                    
                          $film_id[] = $row[film_id];
                          
                          }

// elimino i duplicati per evitare che lo stesso film_id si presenti piu' volte
$film_id=array_unique($film_id);

// eseguo una query per ognuno dei valori contenuti nell'array $film_id 

foreach ($film_id as $id) {
                            
                            $res=mysql_query(SELECT film.id as film_id, generi.descrizione as genre, lingue.descrizione                           
                                                                       as language, film. year
                                                           FROM  // [....]  evito di scrivere tutta la lista di join
                                                           WHERE film.id = $id;)

// il codice continua con il fetch dei risultati simile a quanto descritto nel post precedente
                            
                                 while ($row = mysql_fetch_assoc($res)){
                            

                                        if ($row[film_id] == $check )  {

                                                      if (!in_array ($row[genre],$film[genre])){
                                                      $film[genre][] = $row[genre];
                                                      }
                                      
                                                      if (!in_array ($row[language],$film[language])){
                                                      $film[language][] = $row[language];
                                                      }

                                             }
                                   
                                        else

                                        $film = array (      'id' => $row[film_id],
                                                                    'title' => $row[title],
                                                                    'year' => $row[year],
                                                                    'genre' => array ($row[genre]),
                                                                    'language' => array($row[language])
                                                              );

                                         $check = $row[film_id];

                             }

// qui scrivo l'output html dell'array $film (evito di riportare il codice)

// chiudo il ciclo foreach
                }

Spero vivamente di non essere stato troppo confusionario....

ciao a tutti

salomone

risposto 8 anni fa
salomone
modificato 8 anni fa
X 0 X

Se nell'array $film usi come chiave l'id del film, allora non avrai bisogno del controllo

 if ($row[film_id] == $check )

ma ti basterà un

if(isset($film[$row[film_id]]))

Per il secondo problema poi seguire 2 strade:

1) se vuoi fare una sola query allora devi fare 2 JOIN con la tabella FILM_GENERI, la prima volta per prendere solo i record con un determinato genere, la seconda per prendere tutti gli altri generi.

2) puoi fare una query per prendere solo i record che ti interessano e poi, per ciascuno di questi, devi fare delle query per recuperare tutti i generi associati.

 :bye:

risposto 8 anni fa
Gianni Tomasicchio
X 0 X

grazie mille! ma....ho capito 2 punti su 3.

non mi e' chiaro come fare le due JOIN per ottenere i risultati con una query unica quando l'utente seleziona un genere specifico

la mia query e'

SELECT f.film_id, f.titolo, f.anno, g.descrizione as genere, l.descrizione as lingue

FROM film as f

LEFT JOIN film_generi as fg ON f.film_id=fg.film_id

LEFT JOIN generi as g ON fg,generi_id=g.id

LEFT JOIN film_lingue as fl ON f.film_id=fl.film_id

LEFT JOIN lingue as l ON fl.lingue_id=l.id

WHERE fg.generi_id= '2';

qual e' la join che mi permette di estrarre ANCHE i record per in cui, mettiamo, film_id '3' e' associato a mg.generi_id '4'?

FILM (id, titolo, anno)

GENERI (id, descrizione)

FILM_GENERI (film_id, generi_id)

LINGUE (id, descrizione)

FILM_LINGUE (film_id, lingue_id)

risposto 8 anni fa
salomone
X 0 X

Qualcosa del genere:

SELECT *
FROM film F
JOIN film_generi FG1 ON F.film_id = FG1.film_id AND FG1.generi_id = 4
JOIN film_generi FG2 ON F.film_id = FG2.film_id
JOIN generi G ON FG2.generi_id = G.id
risposto 8 anni fa
Gianni Tomasicchio
X 0 X

ciao gianni

la query che mi suggerisci in effetti funziona, ma ancora non capisco come fare a crearla "dinamicamente" in base alle selezioni dell'utente nel form

devo fare una LEFT JOIN del tipo

JOIN film_generi FG1 ON F.film_id = FG1.film_id AND FG1.generi_id = 4

per tutti i generi possibili (quindi con generi_id=1,2,34, etc) se nell'array $_POST mi trovo almeno un genere selezionato?

risposto 8 anni fa
salomone
X 0 X

Se l'utente può selezionare più generi (es..: genere_id = 1, 3, 4) allora procedi in questo modo:

SELECT *
FROM film F
JOIN film_generi FG1 ON F.film_id = FG1.film_id AND FG1.generi_id = IN (1, 3, 4)
JOIN film_generi FG2 ON F.film_id = FG2.film_id
JOIN generi G ON FG2.generi_id = G.id

 :bye:

risposto 8 anni fa
Gianni Tomasicchio
X 0 X
Effettua l'accesso o registrati per rispondere a questa domanda