GROUP_CONCAT not working as expected - mysql

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)

Related

sql string can't get right

I'm trying to get some data from my db.
It kinda looks like this
GROUPS
groups_id, groups_name, groups_description, groups_active, groups_hash, groups_entry_date, user_id, groups_email, groups_sms
CUSTOMERS_GROUPS
customers_hash, groups_hash
CUSTOMERS
customers_id, customers_first_name, customers_surname, customers_telephone, customers_email, customers_telephone_active, customers_email_active, client_type, customers_hash, customers_entry_date
I want customers.groups_hash and groups.groups_name in a concat form. Here is my attempt ...
SELECT * , GROUP_CONCAT( DISTINCT customers_groups.groups_hash
SEPARATOR '/' ) , GROUP_CONCAT( groups.groups_name
SEPARATOR '/' )
FROM customers
INNER JOIN customers_groups ON ( customers.customers_hash = customers_groups.customers_hash )
LEFT JOIN groups ON ( customers_groups.customers_hash = groups.groups_hash )
WHERE groups.groups_active ='1' GROUP BY customers.customers_entry_date
but it gives me back a zero set ...
The problem is your where clause. It must be part of the on clause:
SELECT * , GROUP_CONCAT( DISTINCT customers_groups.groups_hash
SEPARATOR '/' ) , GROUP_CONCAT( groups.groups_name
SEPARATOR '/' )
FROM customers
INNER JOIN customers_groups ON ( customers.customers_hash = customers_groups.customers_hash )
LEFT JOIN groups ON ( customers_groups.customers_hash = groups.groups_hash ) AND groups.groups_active ='1'
GROUP BY customers.customers_entry_date
The problem is here:
LEFT JOIN groups ON ( customers_groups.customers_hash = groups.groups_hash )
which should probably be
LEFT JOIN groups ON ( customers_groups.groups_hash = groups.groups_hash )
(While it remains unclear what the hashes actually represent and why there is no bridge table linking the tables' IDs instead. I've asked that question in the comment section to your request.)

Nested GROUP_CONCAT 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

Count number of occurrences based on user id

I'm very new to SQL/MySQL and Stackoverflow for that matter, and I'm trying to create a query through iReport (though I don't have to use iReport) for SugarCRM CE. What I need is to create a report that displays the number of "Referrals", "Voicemails", "Emails", and "Call_ins" that are linked to a specific "user" (employee). The query I currently have set up works; however it is running through the data multiple times generating a report that is 200+ pages. This is the code that I am currently using:
SELECT
( SELECT COUNT(*) FROM `leads` INNER JOIN `leads_cstm` ON `leads`.`id` = `leads_cstm`.`id_c` WHERE (leadtype_c = 'Referral' AND users.`id` = leads.`assigned_user_id`) ),
( SELECT COUNT(*) FROM `leads` INNER JOIN `leads_cstm` ON `leads`.`id` = `leads_cstm`.`id_c` WHERE (leadtype_c = 'VM' AND users.`id` = leads.`assigned_user_id`) ),
( SELECT COUNT(*) FROM `leads` INNER JOIN `leads_cstm` ON `leads`.`id` = `leads_cstm`.`id_c` WHERE (leadtype_c = 'Email' AND users.`id` = leads.`assigned_user_id`) ),
users.`first_name`,users.`last_name`
FROM
`users` users,
`leads` leads
I would appreciate any guidance!
You want to use conditional summation. The following uses MySQL syntax:
SELECT sum(leadtype_c = 'Referral') as Referrals,
sum(leadtype_c = 'VM') as VMs,
sum(leadtype_c = 'Email') as Emails,
users.`first_name`, users.`last_name`
FROM users join
`leads`
on users.`id` = leads.`assigned_user_id` INNER JOIN
`leads_cstm`
ON `leads`.`id` = `leads_cstm`.`id_c`
group by users.id;
You can use COUNT with CASE for this:
SELECT u.first_name,
u.last_name,
count(case when leadtype_c = 'Referral' then 1 end),
count(case when leadtype_c = 'VM' then 1 end),
count(case when leadtype_c = 'Email' then 1 end)
FROM users u
JOIN leads l ON u.id = l.assigned_user_id
JOIN leads_cstm lc ON l.id = lc.id_c
GROUP BY u.id
To match your exact results, you should probably use an OUTER JOIN instead, but this gives you the idea.
A Visual Explanation of SQL Joins

Slow MySQL query with subquery from table

I am trying to bring back a string based on an IF statement but it is extremely slow.
It has something to do with the first subquery but I am unsure of how to rearrange this as to bring back the same results but faster.
Here is my SQL:
SELECT IF
(
(
SELECT COUNT(*)
FROM
(
SELECT DISTINCT enquiryId, type
FROM parts_enquiries, parts_service_types AS pst
WHERE parts_enquiries.serviceTypeId = pst.id
) AS parts
WHERE parts.enquiryId = enquiries.id
) > 1, 'Mixed',
(
SELECT DISTINCT type
FROM parts_enquiries, parts_service_types AS pst
WHERE parts_enquiries.serviceTypeId = pst.id AND enquiryId = enquiries.id
)
) AS partTypes
FROM enquiries,
entities
WHERE enquiries.entityId = entities.id
How can I make it faster?
I have modified my original query below, but I am getting the error that subquery returns more than one row:
SELECT
(SELECT
CASE WHEN COUNT(DISTINCT type) > 1 THEN 'Mixed' ELSE `type` END AS type
FROM parts_enquiries
INNER JOIN parts_service_types AS pst ON parts_enquiries.serviceTypeId = pst.id
INNER JOIN enquiries ON parts_enquiries.enquiryId = enquiries.id
INNER JOIN entities ON enquiries.entityId = entities.id
GROUP BY enquiryId) AS partTypes
FROM enquiries,
entities
WHERE enquiries.entityId = entities.id
Please have a look if this query yields the same results:
SELECT
enquiryId,
CASE WHEN COUNT(DISTINCT type) > 1 THEN 'Mixed' ELSE `type` END AS type
FROM parts_enquiries
INNER JOIN parts_service_types AS pst ON parts_enquiries.serviceTypeId = pst.id
INNER JOIN enquiries ON parts_enquiries.enquiryId = enquiries.id
INNER JOIN entities ON enquiries.entityId = entities.id
GROUP BY enquiryId
But N.B.'s comment is still valid. To see if and index is used and other information we need to see the EXPLAIN and the table definitions.
This should get you what you want.
I would first pre-query your parts enquiries and parts service types looking for both the count and MINIMUM of the part 'type', grouped by the enquiry ID.
then, run your IF() against that result. If the distinct count is > 0, then 'Mixed'. If only one, since I did the MIN(), it would only have the description of that one value that you desire anyhow.
SELECT
E.ID
IF ( PreQuery.DistTypes > 1, 'Mixed', PreQuery.FirstType ) as PartType
from
Enquiries E
JOIN ( SELECT
PE.EnquiryID,
COUNT( DISTINCT PE.ServiceTypeID ) as DistTypes,
MIN( PST.Type ) as FirstType
from
Parts_Enquiries PE
JOIN Parts_Service_Types PST
ON PE.ServiceTypeID = PST.ID
group by
PE.EnquiryID ) as PreQuery
ON E.ID = PreQuery.EnquiryID

Getting usernames in forum topic information from linked tables

For a forum i want to fetch a forumTopic with additional, linked information like the lastPostDate, lastPostUserName and starterUserName.
The problem arises with the lastPostUserName and starterUserName. When a forumTopic has only one linked post it seems to work correctly and both the lastPostUserName as the starterUserName are filled. When there are multiple posts linked to a topic only the starterUserName is filled and the lastPostUserName is NULL
The structure of the database is a formCategory has a number of formTopic the forumTopic has a number of forumPost and a forumPost is linked to a user.
SELECT forumTopic.*,
COUNT( forumPost.id ) AS postCount,
MAX(forumPost.date) AS lastPostDate,
(SELECT name FROM user AS u1 WHERE u1.id = forumPost.posterUserId AND forumPost.date = MAX(forumPost.date) )
AS lastPostUserName,
(SELECT name FROM user AS u2 WHERE u2.id = forumPost.posterUserId AND forumPost.date = MIN(forumPost.date) )
AS starterUserName
FROM forumCategory
LEFT JOIN forumTopic ON forumCategory.id = forumTopic.forumCategoryId
LEFT JOIN forumPost ON forumPost.forumTopicId = forumTopic.id
WHERE forumCategory.rewrittenName='someforumcategory'
AND forumCategory.active='Y'
AND forumTopic.active='Y'
AND forumPost.active='Y'
GROUP BY forumTopic.id
ORDER BY forumPost.date ASC
Try this
SELECT forumTopic.*,
innerv.*,
(SELECT name FROM user AS u1 WHERE u1.id = innerv.first_user)
AS startedUserName,
(SELECT name FROM user AS u2 WHERE u2.id = innerv.last_user )
AS lastUserName
FROM forumTopic
LEFT JOIN forumCategory ON forumCategory.id = forumTopic.forumCategoryId
LEFT JOIN (
SELECT forumTopicId, MAX(date) AS LAST_POSTED_dATE, MIN(date) as FIRST_POSTED_DATE,
SUBSTRING_INDEX(
GROUP_CONCAT(posterUserId ORDER BY date),
',',
1
) as first_user,
SUBSTRING_INDEX(
GROUP_CONCAT(posterUserId ORDER BY date),
',',
-1
) as last_user, count(1) as posts_under_topic
FROM forumPost where forumPost.active='Y'
GROUP BY forumTopicId ) innerv ON innerv.forumTopicId = forumTopic.id
WHERE forumCategory.rewrittenName='someforumcategory'
AND forumCategory.active='Y'
AND forumTopic.active='Y'
The subquery (innerv) filter active records and groups the records in the forumPost by topicId.