简体   繁体   中英

Echo mysql data using group_concat in PHP

This is my SQL Fiddle: SQL Fiddle

I am trying to echo this result using PHP. This is my expected result:

// Some Stuff Here

The Dark Knight Rises -  7.5
Batman Begins - 7.5 
Iron Man - 7.3
The Lord of the Rings: The Return of the King - 8.1 
etc...

// Some more Stuff here

My PHP Code:

$stmt = $conn->prepare("SELECT tmdb_movies.movie_title
,GROUP_CONCAT(recommendations.recommendations_title  ORDER BY recommendations.recommendations_title) as recommendations_title
,GROUP_CONCAT(recommendations.recommendations_vote_average ORDER BY recommendations.recommendations_title) as recommendations_vote_average

FROM tmdb_movies 

LEFT JOIN cast ON cast.cast_tmdb_id=tmdb_movies.tmdb_id
LEFT JOIN recommendations ON recommendations.recommendations_tmdb_id=tmdb_movies.tmdb_id


Where tmdb_movies.tmdb_id= 155

GROUP BY tmdb_movies.movie_title
 ");


     // Then fire it up
     $stmt->execute();
     // Pick up the result as an array
     $result = $stmt->fetchAll();

    // Now you run through this array in many ways, for example
     for($x=0, $n=count($result); $x < $n; $x++){
$recommendations_name_list = explode(',',$result[$x]["recommendations_title"]);
$recommendations_vote_average = explode(',',$result[$x]["recommendations_vote_average"]);
foreach( $recommendations_name_list as $index => $recommendations_title ) {
           echo'
    <p>'.$recommendations_title.'- '.$recommendations_vote_average[$index].'</p>';
    }
}


Output of this code:

 Batman Begins- 7.5 Batman Begins- 7.5 Batman Begins- 7.5 Batman Begins- 7.5 Batman Begins- 7.5 Batman Begins- 7.5 Batman Begins- 7.5 Batman Begins- 7.5 Batman Begins- 7.5 Batman Begins- 7.5 Captain America: The First Avenger- 6.6 Captain America: The First Avenger- 6.6 Captain America: The First Avenger- 6.6 Captain America: The First Avenger- 6.6 Captain America: The First Avenger- 6.6 Captain America: The First Avenger- 6.6 Captain America: The First Avenger- 6.6 Captain America: The First Avenger- 6.6 Captain America: The First Avenger- 6.6 Captain America: The First Avenger- 6.6 Inception- 8 Inception- 8 Inception- 8 Inception- 8 Inception- 8 Inception- 8 Inception- 8 Inception- 8 Inception- 8 Inception- 8 Iron Man- 7.3 Iron Man- 7.3 Iron Man- 7.3 Iron Man- 7.3 Iron Man- 7.3 Iron Man- 7.3 Iron Man- 7.3 Iron Man- 7.3 Iron Man- 7.3 Iron Man- 7.3 Iron Man 2- 6.6 Iron Man 2- 6.6 Iron Man 2- 6.6 Iron Man 2- 6.6 Iron Man 2- 6.6 Iron Man 2- 6.6 Iron Man 2- 6.6 Iron Man 2- 6.6 Iron Man 2- 6.6 Iron Man 2- 6.6 The Dark Knight Rises- 7.5 The Dark Knight Rises- 7.5 The Dark Knight Rises- 7.5 The Dark Knight Rises- 7.5 The Dark Knight Rises- 7.5 The Dark Knight Rises- 7.5 The Dark Knight Rises- 7.5 The Dark Knight Rises- 7.5 The Dark Knight Rises- 7.5 The Dark Knight Rises- 7.5 The Lord of th- 8 

But If i remove this line LEFT JOIN cast ON cast.cast_tmdb_id=tmdb_movies.tmdb_id then, my php code works fine.

Expected Result:

 Batman Begins- 7.5 Captain America: The First Avenger- 6.6 Inception- 8 Iron Man- 7.3 Iron Man 2- 6.6 The Dark Knight Rises- 7.5 The Lord of the Rings: The Fellowship of the Ring- 8 The Lord of the Rings: The Return of the King- 8.1 The Lord of the Rings: The Two Towers- 7.9 The Matrix- 7.9 

The use of the LEFT JOIN cast
is unuseful for the select values (the results are select ayway because are based on maun table movie_title adn don't return useful info because there are no columns from di table in select clause ) and produce a resulting set with more rows

so if you want insist on thi left join you can add DISTINCT to your originale query

 SELECT DISTICNT 
  tmdb_movies.movie_title
  ,GROUP_CONCAT(recommendations.recommendations_title  
        ORDER BY recommendations.recommendations_title) as recommendations_title
  ,GROUP_CONCAT(recommendations.recommendations_vote_average 
      ORDER BY recommendations.recommendations_title) as recommendations_vote_average

FROM tmdb_movies 

LEFT JOIN cast ON cast.cast_tmdb_id=tmdb_movies.tmdb_id
LEFT JOIN recommendations 
    ON recommendations.recommendations_tmdb_id=tmdb_movies.tmdb_id
Where tmdb_movies.tmdb_id= 155
GROUP BY tmdb_movies.movie_title

or better don use left join pn cast

   SELECT 
    tmdb_movies.movie_title
    ,GROUP_CONCAT(recommendations.recommendations_title  
          ORDER BY recommendations.recommendations_title) as recommendations_title
    ,GROUP_CONCAT(recommendations.recommendations_vote_average 
        ORDER BY recommendations.recommendations_title) as recommendations_vote_average

  FROM tmdb_movies 


  LEFT JOIN recommendations 
      ON recommendations.recommendations_tmdb_id=tmdb_movies.tmdb_id
  Where tmdb_movies.tmdb_id= 155
  GROUP BY tmdb_movies.movie_title

Many of the things you have in your post don't add up. Your sql fiddle is missing the cast table entirely. I have already commented that you are using GROUP_CONCAT inappropriately. Your question does not have the complete information to help us understand your problem.

With that said, I believe I have surmised something close to what you actually want. The main problem is that your GROUP BY should include both the original movie AND the recommended movies. This will return you one row per group combination of movie/recommendation. You can use GROUP_CONCAT to get the cast in one column. Furthermore, I think that the cast data you want is actually the cast of the recommended movie, not the cast for the original movie. You could get both together, but in the context of this query, it seems to me that you would want the cast of the recommended movie assembled into one column with GROUP_CONCAT. As I commented to you, the problem is that you don't have the recommended movie's tmdb_id in the recommendation table. Quite frankly that is a denormalized structure (storing the title rather than the id) and is relationally incorrect, wasteful and potentially confusing.

Why is this?

First off, the recommendation table has no Primary key and it needs one. Title is not really a good candidate key either, because there are many movies that have the same title. With your system there is no way to determine which film out of several that may have the same title. Thus what you should have, is the tmdb_id as a foreign key, and at that point, you would not need (nor want) the title, because it could be gotten from the tmdb_movies table. Your recommendation table is essentially a many-to-many table that relates movies to each other in a parent to child manner. Aside from the foreign keys (tmdb_id, recommended_tmdb_id) there doesn't actually need to be anything else in the table, since the values you currently have in there (title, avg rating) are denormalized values that are stored in the tmdb_movies table.

Now think ahead to the future, when perhaps you have thousands of movies, each of which may have 10 recommended movies related to it. You will be wasting for any one movie, megabytes of database storage repeating the same title and average value that doesn't change. Worse yet, averages are a snapshot in time. Whenever the average rating needs to be recalculated, rather than updating a single value in one row of the tmdb_movie table, you now have to update every single row in recommendation where that movie was recommended to the original. These are some of the costs of de-normalization.

Now to the specifics of GROUPing.

First to illustrate the way that GROUP BY works, I suggest you run these queries in your sqlfiddle or live db:

SELECT tmdb_movies.movie_title, recommendations.recommendations_title, recommendations.recommendations_vote_average
FROM tmdb_movies 
LEFT JOIN recommendations ON recommendations.recommendations_tmdb_id=tmdb_movies.tmdb_id
GROUP BY tmdb_movies.tmdb_id, recommendations.recommendations_title
Where tmdb_movies.tmdb_id=1

The key thing that is different from what you've been doing, is that I removed the useless GROUP_CONCAT statements, and did a GROUP BY on tmdb_movies.tmdb_id, recommendations.recommendations_title.

Ideally this would be by the id's but I covered that above, so for now we have to just use title.

As you can see, you get a GROUP for each unique combination of Movie & Recommended Movie. It appears this is what you want. Notice that I didn't used GROUP_CONCAT at all, and you get the average and the title, one per row.

Now let's assume you want to also join to the cast table.

Let's add that in:

SELECT tmdb_movies.movie_title, recommendations.recommendations_title, recommendations.recommendations_vote_average
FROM tmdb_movies 
LEFT JOIN recommendations ON recommendations.recommendations_tmdb_id=tmdb_movies.tmdb_id
LEFT JOIN cast ON cast.recommendations_title=recommendations.recommendations_title
GROUP BY tmdb_movies.tmdb_id, recommendations.recommendations_title

Notice that NOTHING CHANGES. You do not get extraneous rows, because the GROUP BY already reduces duplicates and leaves one row per group, even though we are now joining movie->recommendation->cast

So, now I will assume you want the cast in one column in the result. Here is where GROUP_CONCAT actually comes in useful and seems appropriate:

SELECT tmdb_movies.movie_title, recommendations.recommendations_title, recommendations.recommendations_vote_average, GROUP_CONCAT(cast.name)
FROM tmdb_movies 
LEFT JOIN recommendations ON recommendations.recommendations_tmdb_id=tmdb_movies.tmdb_id
LEFT JOIN cast ON cast.recommendations_title=recommendations.recommendations_title
GROUP BY tmdb_movies.tmdb_id, recommendations.recommendations_title

Here's the sqlfiddle to this :

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM