Nested GROUP_CONCAT MySQL? - mysql

Ok, say I have a database layout similar to the one in the example I have provided. In the above examples movies have releases in languages and those movie_releases also have theatre releases in different theatres.
Now if I wanted to query the database to get all of a movie's theatre releases as well as all movie_releases, how would I do that?
The current code I have is:
select
GROUP_CONCAT(movie.name, ",", language.lang, ",", GROUP_CONCAT(theatre.name)) as releases
from
movie
left join movie_releases on movie_releases.movie_id = movie.id
left join language on movie_release.language_id = language.id
left join theatre_release on theatre_release.movie_release_id = movie_release.id
left join theatre on theatre.id = theatre_release.theatre_id
Obviously this doesn't work because you can't nest GROUP_CONCAT functions like this.
My desired output would be something like
["Spiderman", "English", ["Cinema Theatre", "Garden Cinemas"]]
["Spiderman", "Spanish", ["El Cine Grande"]]
["Batman", "English", ["Town Movies"]]

SELECT
CONCAT(
'["', `movie`.`name`, '"',
', ',
'"', releases.lang, '"',
', [',
releases.theatres,
']]'
) AS `release`
FROM
movie
LEFT JOIN (
SELECT
movie_id,
`language`.lang,
GROUP_CONCAT('"', theatre.`name`, '"') AS theatres
FROM
movie_release
LEFT JOIN theatre_release ON theatre_release.movie_release_id = movie_release.id
LEFT JOIN theatre ON theatre.id = theatre_release.theatre_id
LEFT JOIN `language` ON `language`.id = movie_release.language_id
GROUP BY
movie_id,
language_id
) AS releases ON releases.movie_id = movie.id
You can group the theatres in a subselect and then join them onto the movies by their relation over the releases.
By bringing the data in your format it might make the query a little harder to read. So here is the query without the fancy formatting.
SELECT
CONCAT(
`movie`.`name`,
' ',
releases.lang,
' ',
releases.theatres
) AS `release`
FROM
movie
LEFT JOIN (
SELECT
movie_id,
`language`.lang,
GROUP_CONCAT(theatre.`name`) AS theatres
FROM
movie_release
LEFT JOIN theatre_release ON theatre_release.movie_release_id = movie_release.id
LEFT JOIN theatre ON theatre.id = theatre_release.theatre_id
LEFT JOIN `language` ON `language`.id = movie_release.language_id
GROUP BY
movie_id,
language_id
) AS releases ON releases.movie_id = movie.id
To add the delimiteres you only have to adjust the CONCAT or the GROUP_CONCAT

Related

Group concat Query Performance

I have one query which almost took 22 second to populate data.
SELECT
TP.*,
GROUP_CONCAT(DISTINCT TS.`subject_name`) AS subjects,
GROUP_CONCAT(DISTINCT TC.`class_name`) AS classes,
GROUP_CONCAT(DISTINCT TT.`tution_name`) AS tution_type,
GROUP_CONCAT(DISTINCT TL.`name`) AS locations
FROM `tutor_profile` TP
LEFT JOIN `tutor_to_subject` TTS ON TP.`tutor_id`=TTS.`tutor`
LEFT JOIN `tutor_subjects` TS ON TS.`subject_id`=TTS.`subject`
LEFT JOIN `tutor_to_class` TTC ON TP.`tutor_id`=TTC.`tutor`
LEFT JOIN `tutor_classes` TC ON TC.`class_id`=TTC.`class`
LEFT JOIN `tutor_to_tution_type` TTTT ON TP.`tutor_id`=TTTT.`tutor`
LEFT JOIN `tution_types` TT ON TT.`tution_id`=TTTT.`tution_type`
LEFT JOIN `tutor_to_locality` TTL ON TP.`tutor_id`=TTL.`tutor`
LEFT JOIN `tutor_locality` TL ON TL.`id`=TTL.`locality`
WHERE 1=1 AND TP.`status` = 1
GROUP BY TP.`tutor_id`
ORDER BY TP.`date_added` DESC LIMIT 0 , 25
is there anyway to improve its performance?
You may try to use correlated subqueries in the output list instead of JOIN and GROUP BY:
SELECT
TP.*,
( SELECT GROUP_CONCAT(DISTINCT TS.`subject_name`)
FROM `tutor_to_subject` TTS
LEFT JOIN `tutor_subjects` TS ON TS.`subject_id`=TTS.`subject`
WHERE TP.`tutor_id`=TTS.`tutor` ) AS subjects,
-- the same for another output columns
FROM `tutor_profile` TP
WHERE 1=1 AND TP.`status` = 1
ORDER BY TP.`date_added` DESC LIMIT 0 , 25
Replace these
GROUP_CONCAT(DISTINCT TS.`subject_name`) AS subjects,
LEFT JOIN `tutor_to_subject` TTS ON TP.`tutor_id`=TTS.`tutor`
LEFT JOIN `tutor_subjects` TS ON TS.`subject_id`=TTS.`subject`
with
( SELECT GROUP_CONCAT(DISTINCT TS.`subject_name`
FROM `tutor_to_subject` TTS
JOIN `tutor_subjects` TS ON TS.`subject_id` = TTS.`subject`
WHERE TP.`tutor_id` = TTS.`tutor`
) AS subjects,
and toss the JOIN for TS. Do likewise for the other 3 group_concat + Left join.
For many-to-many tables, improve the indexes:
CREATE TABLE tutor_to_subject
tutor_id ...,
subject_id ...,
PRIMARY KEY(tutor_id, subject_id),
INDEX(subject_id)
) ENGINE=InnoDB;
More discussion: Many:many mapping
Also, get rid of the GROUP BY since it should not longer be needed.
And add
INDEX(status, date_added)

How to query multiple weak tables using a join

I have a database with two strong entities, and three weak entities. The weak entities only have foreign key references to the primary key of the strong tables they related to, to create an association between the tables.
I have a query selecting from one table the way I want, but am not sure how to go about selecting from all three tables at once and displaying the data in one query.
ERD:
I have tried many different joins and can not get them to work properly. This select displays the information that I want for one table:
select f.FilmName, concat(p.FirstName, ' ', p.LastName) as Producer
from filmtable as f
inner join persontable as p
inner join produced;
I would like to be able to select and display all the information from all three weak tables, in a format similar to:
FilmName, Producer, ScreenWriter, Director.
You need:
select f.FilmName, concat(ppr.FirstName, ' ', ppr.LastName) as Producer,
concat(psw.FirstName, ' ', psw.LastName) as ScreenWriter,
concat(pdir.FirstName, ' ', pdir.LastName) as Director
from filmtable as f
left join produced as prod ON prod.FilmID = f.FilmID
left join Directed as dir ON dir.FilmID = f.FilmID
left join ScreenWote as sw ON sw.FilmID = f.FilmID
left join persontable as ppr ON prod.PersonID = ppr.personID
left join persontable as psw ON sw.PersonID = psw.personID
left join persontable as pdir ON dir.PersonID = pdir.personID;
Alternate solution is to use sub queries in SELECT statement to achieve all 3 parties i.e. Producer, Writer and Director, in one row.
SELECT f.FilmName,
(
SELECT concat(p.FirstName, ' ', p.LastName)
FROM persontable AS pt
JOIN produced AS a
ON pt.PersonID = a.PersonID
WHERE p.FilmID = f.FilmID
) AS producer,
(
SELECT concat(p.FirstName, ' ', p.LastName)
FROM persontable AS pt
JOIN screenwrote AS a
ON pt.PersonID = a.PersonID
WHERE p.FilmID = f.FilmID
) AS ScreenWriter,
(
SELECT concat(p.FirstName, ' ', p.LastName)
FROM persontable AS pt
JOIN directed AS a
ON pt.PersonID = a.PersonID
WHERE p.FilmID = f.FilmID
) AS Director
FROM filmtable AS f

Why group_concat return too recording for the first row?

I have an sql request and in this one I use function GROUP_CONCAT. I use this to have a list of delegue in each row.
This is my request:
SELECT pro.company, pro.title AS person_title, pro.name AS person_name, pro.firstname AS person_firstname, pro.address, pro.address2, pro.postcode, cities.name AS city, pro.phone, pro.gsm, pro.fax, pro.email, pro.client_number AS sap, GROUP_CONCAT( DISTINCT user.firstname, ' ', user.name SEPARATOR ', ') AS delegue, agency.name AS agency, CONCAT(drv.firstname, ' ', drv.name) AS drv_name, drv.email AS drv_email, GROUP_CONCAT(DISTINCT pc.competence_id SEPARATOR ',') AS competences
FROM velux.professionnal pro
INNER JOIN velux.cities ON cities.id = pro.city_id
INNER JOIN velux.zone ON pro.postcode BETWEEN zone.postcode_start AND postcode_end
LEFT JOIN velux.p2p_user_zones user_zones ON user_zones.zone_id = zone.id
LEFT JOIN velux.p2p_user user ON user.id = user_zones.user_id
left join velux.p2p_user_group on p2p_user_group.user_id = user.id
LEFT JOIN velux.agency ON zone.agency_id = agency.id
LEFT JOIN velux.p2p_user drv ON drv.id = agency.user_id
LEFT JOIN velux.professionnal_competence pc ON pc.professionnal_id = pro.id
WHERE p2p_user_group.group_id in (10, 3)
GROUP BY pc.professionnal_id
My problem is in the first row of my records the column delegue has too much result as expected :(. But for the other row it's ok. I don't know what is this bug and I don't know how to resolve it.
Someone have an idea ? If yes it would be great ! Thank in advance !

GROUP_CONCAT not working as expected

I have a many to many relationship. Projects table, projects_users table, and users table. I am trying to return a listing of projects, with its associated users. Here is the query I'm using, which works, but only shows a single user, when I know there should be more:
SELECT
projects.id,
`projects`.`project_name`,
( GROUP_CONCAT(DISTINCT `users`.`name` separator ', ' ) ) AS staff,
FROM `projects`
INNER JOIN `projects_users` ON (`projects_users`.`project_id` = `projects`.`id` )
INNER JOIN `users` ON (`users`.`id` = `projects_users`.`user_id` )
GROUP BY projects.id, `projects_users`.`user_id`
HAVING (`projects_users`.`user_id` = 8)
There are several users associated with each project and I would expect to return something like "User 1, User 2, User 3". Instead, I only get "User 1."
Since you need to aggregate the users per project, you should group according to the user_id:
SELECT
projects.id,
`projects`.`project_name`,
( GROUP_CONCAT(DISTINCT `users`.`name` separator ', ' ) ) AS staff
FROM projects -- was missing in the OP
INNER JOIN `projects_users` ON (`projects_users`.`project_id` = `projects`.`id` )
INNER JOIN `users` ON (`users`.`id` = `projects_users`.`user_id` )
GROUP BY projects.id -- Group by fixed here
HAVING (`projects_users`.`user_id` = 8)

Multiple many-to-many JOINs in a single mysql query without Cartesian Product

At the moment I can get the results I need with two seperate SELECT statements
SELECT
COUNT(rl.refBiblioID)
FROM biblioList bl
LEFT JOIN refList rl ON bl.biblioID = rl.biblioID
GROUP BY bl.biblioID
SELECT
GROUP_CONCAT(
CONCAT_WS( ':', al.lastName, al.firstName )
ORDER BY al.authorID )
FROM biblioList bl
LEFT JOIN biblio_author ba ON ba.biblioID = bl.biblioID
JOIN authorList al ON al.authorID = ba.authorID
GROUP BY bl.biblioID
Combining them like this however
SELECT
GROUP_CONCAT(
CONCAT_WS( ':', al.lastName, al.firstName )
ORDER BY al.authorID ),
COUNT(rl.refBiblioID)
FROM biblioList bl
LEFT JOIN biblio_author ba ON ba.biblioID = bl.biblioID
JOIN authorList al ON al.authorID = ba.authorID
LEFT JOIN refList rl ON bl.biblioID = rl.biblioID
GROUP BY bl.biblioID
causes the author result column to have duplicate names. How can I get the desired results from one SELECT statement without using DISTINCT? With subqueries?
If subselects are acceptable, I believe you could make this happen by doing something like this:
SELECT
y.authors,
COUNT(rl.refBiblioID)
FROM
(SELECT
bl.biblioId,
GROUP_CONCAT(
CONCAT_WS(':', al.lastName, al.firstName)
ORDER BY al.authorID) AS authors
FROM biblioList bl
LEFT JOIN biblio_author ba ON ba.biblioID = bl.biblioID
JOIN authorList al ON al.authorID = ba.authorID
GROUP BY bl.biblioID) y
LEFT JOIN refList rl ON (y.biblioID = rl.biblioID)
GROUP BY y.biblioID
Another solution would be to add DISTINCT to your GROUP_CONCAT, but that was not what you wanted?
GROUP_CONCAT(
DISTINCT(CONCAT_WS(':', al.lastName, al.firstName) ORDER BY al.authorID)),