MySQL GROUP_CONCAT and Multiple Lookup Tables - mysql

I Have seven tables I am trying to join:
Table programs
Column id program_name program_description
1 Self Help Self Help Description...
2 Wellness Wellness Description...
3 Education Education Description...
______________________________________________________
Table county
Column id county_name
1 Stark
2 Portage
3 Wayne
_________________________
Table services
Column id service_name service_description
1 Counseling Counseling Description...
2 Group Therapy Group Therapy Description...
3 Evaluation Evaluation Description...
__________________________________________________________
Table population
Column id population_name
1 Adults
2 Children
3 Youth
_____________________________
Table program_county
Column id program_id county_id
1 1 2
2 1 3
3 2 1
4 2 2
_____________________________________
Table program_service
Column id program_id service_id
1 1 2
2 1 3
3 2 1
4 2 2
5 2 3
______________________________________
Table program_population
Column id program_id population_id
1 1 3
2 2 1
3 2 3
4 3 1
_________________________________________
I am trying to write a query that would return one row for each program and retrieve the related rows in program_county,program_services, and program_population tables and lookup the names of those services, population and counties display them in one field each. Like:
id program_name program_description Counties Services Population Served
1 Self Help Self Help Description... Portage, Wayne Group Therapy, Evaluation Youth
2 Wellness Wellness Description Stark, Portage Counseling, Group Therapy, Evaluation Adults, Youth
I know I have to use joins and GROUP_CONCAT but I am admittedly very lost.

You'll want to use outer joins if you want to see all programs regardless of whether they are associated to counties, services, or populations. Likewise you should use coalesce() around the values you are concatenating to handle NULL values properly.
Something like this should work:
select programs.id, programs.program_name, programs.program_description,
group_concat(distinct coalesce(county.county_name,'')) as "Counties",
group_concat(distinct coalesce(services.service_name,'')) as "Services",
group_concat(distinct coalesce(population.population_name,'')) as "Population Served"
from programs
left outer join program_county on program_county.program_id = programs.id
left outer join county on county.id = program_county.county_id
left outer join program_service on program_service.program_id = programs.id
left outer join services on services.id = program_service.service_id
left outer join program_population on program_population.program_id = programs.id
left outer join population on population.id = program_population.population_id
group by programs.id, programs.program_name, programs.program_description
order by programs.id

Related

MySQL substring to self join

I'm defining the relationship between the two tables using a join table. I want to arrange them in the order of many overlapping things. Currently, we are using subquery, is there a way to get the same result using join?
People FoodTable PeopleFood
ID | NAME ID | Food ID | PeopleId | FoodId
1 BOB 1 Hamberger 1 1 1
2 JOHN 2 Pizza 2 1 2
3 KATY 3 Chicken 3 1 3
4 MILLER 4 Salad 4 2 1
5 AMANDA 5 Sushi 5 2 2
6 2 3
7 3 2
8 3 3
9 4 3
10 4 5
11 5 5
When the table is defined in this way, I want to arrange food tastes similar to Bob's.
I'm doing it like this now.
SELECT people_id, COUNT(people_id) as count
FROM peopleFood
WHERE food_id IN
(SELECT food_id FROM peopleFood
WHERE people_id = 1)
AND people_id != 1
GROUP BY people_id
ORDER BY count DESC;
-- Result -------------
People_id | count
2 3
3 2
4 1
Is there a better way to change this method or use join?
Thank you!!!
You have been inconsistent in your use of the table and column names -
Tables - PeopleFood in your sample data but you reference peopleFood in your query.
Columns - PeopleId and FoodId in your sample data but you reference people_id and food_id in your query.
Choose a naming convention and stick to it. Everyone has there own preference but the important thing is to be consistent.
The equivalent query with INNER JOIN instead of your sub-query is -
SELECT
`pf2`.`people_id`,
COUNT(`pf2`.`food_id`) as `count`
FROM `PeopleFood` `pf1`
INNER JOIN `PeopleFood` `pf2`
ON `pf2`.`people_id` <> `pf1`.`people_id`
AND `pf2`.`food_id` = `pf1`.`food_id`
WHERE `pf1`.`people_id` = 1
GROUP BY `pf2`.`people_id`
ORDER BY `count` DESC;
The performance difference between the two queries is unlikely to be noticeable and it might be argued that the intent is clearer in your version with the sub-query.
The surrogate key ID on your PeopleFood table should be dropped in favour of the compound “natural” primary key on people_id and food_id.
The Cost of Useless Surrogate Keys in Relationship Tables
Inner join:
SELECT p.People_id, COUNT(p.People_id) as count FROM PeopleTable p
INNER JOIN FoodTable f
ON(p.People_id = f.FoodId)
WHERE people = 1
GROUP BY p.people_id
ORDER BY count DESC;
If it helps, please mark it as an accepted answer!

Query that provides the names of the recruiters that hire more than 3 employees, and the number of employees that were not hired by a recruiter

I'm a bit stuck on this question and was hoping for some help. Here's where I'm at currently.
I have this TEST table of Names. Each Person can either be a recruiter or an employee. The number in Recruited_by is associated with the person_id.
Person_id Name Recruited_by
1 Jean Grayson 1
2 Paul Smith 7
3 John Do Null
4 Alex Lee 7
5 Lisa Kim 7
6 Bob Thompson 3
7 Mike Keen Null
8 Raymond Red 3
9 Alisson Jones 1
10 Kate James 3
Here is the query I have so far which I'm trying to the names of the recruiters that hire more than 3 employees (which will return nothing in this case) and the number of employees that were NOT recruited by anyone (which would be the NULL names).
SELECT T.Name as Employees, COUNT(T1.Name) as Not_hired
FROM Test AS T
WHERE COUNT(T1.Name) IS NULL
LEFT OUTER JOIN Test AS T1
ON T.Recruited_by = T1.Person_id
GROUP BY T.Name
HAVING COUNT(T1.Name) > 3
However this query is returning nothing when I should expect it to return the number of employees who were not hired by a recruiter!
If you want in the results only 1 row with 2 columns then you can do a LEFT join of the table to a query that aggregates to get the ids of the persons that hired more than 3 persons and aggregate again to get the number of persons that were not recruited by anyone:
SELECT GROUP_CONCAT(CASE WHEN t2.Recruited_by IS NOT NULL THEN t1.Name END ORDER BY t1.Name) names,
SUM(t1.Recruited_by IS NULL) total_not_recruited
FROM Test t1
LEFT JOIN (
SELECT Recruited_by
FROM Test
GROUP BY Recruited_by
HAVING COUNT(*) > 3
) t2 ON t2.Recruited_by = t1.Person_id;
You will get the names of the persons that hired more than 3 persons (if they exist) as a comma separated list.
See the demo.

SQL: How to COUNT the number of keys that appear in a certain number of rows

I have the following two SQL tables dog and toy:
dog
id
gender
1
2
2
2
3
2
4
1
toy
toy_id
dog_id
1
1
1
2
2
1
2
2
We want to find out how many female dogs have a certain 0 toys, 1 toy, 2 toys... A value of 2 in the gender column indicates the dog is female.
The answer should be as seen below because dogs 1 and 2 are both female and have 2 toys, where as dog 3 is female and has 0 toys:
num_toys
num_dogs
0
1
2
2
What SQL query will get me to this answer? The query below gives me a table with all the female dogs and their toys. How do I expand on this to aggregate?
Select toys.toy_id, toys.dog_id
From toys
Where toys.toy_id IN (
SELECT dog.id
From dog
Where dog.gender = 2
)
Hmmm . . . You could use two levels of aggregation:
select num_toys, count(*)
from (select d.id, count(t.dog_id) as num_toys
from dogs d left join
toys t
on t.dog_id = d.id
where d.gender = 2
group by d.id
) d
group by num_toys;
The trick here is to use a let join in the subquery, so you can get dogs with no toys.

How to join multiple queries with different tables and different column name

I want to join multiple queries with different tables and column name, along with I need to display the count of duplicate fields as shown below.
The queries are: (Proj_uid is common in all the tables which I need to match)
select proj_name,Agency,District,Division,Proj_status from tempproj
Need to join 2 tables to get the result that is payment80 and payment20 which contains billtype column with duplicate values, I want to count those value too
SELECT p.Proj_name,p.billtype, COUNT(1) as CNT
FROM payment80 p where billtype='civil'
GROUP BY Proj_name, billtype
(This is by using single table but I want this result by joining both payment80 and payment20 tables)
SELECT p.Proj_name,p.billtype, COUNT(1) as CNT
FROM payment80 p where billtype='Electric'
GROUP BY Proj_name, billtype
(The billtype values I want to count and just display a number of duplicate records)
Proj_Name billtype
------------------------
policegruha civil
gruhayojna Electric
policegruha civil
dcoffice civil
spoffice Electric
dcoffice civil
3) Select billtype from payment, here also I need count the duplicate values and display in billtype
Duplicate values will be in billtype which contains some thing like this:
Finally I want an output like this:
Proj_name Agency District Division Projstatus Civilbilltype Electricbilltype
policegruha kumar chitradurga davangere ongoing 3 1
gruhayojna khan ballary ballary completed 2 2
Atered john bangalore bangalore ongoing 2 4
dcoffice ravi mangalore mangalore ongoing 1 2
spoffice mary chitradurga davangere completed 3 4
hostel jack ballary ballary completed 3 3
univercity kumar bangalore bangalore ongoing 4 2
mess Raj mysore mysore ongoing 2 1
policestation khan mysore mysore ongoing 1 4
conferencehall Rosy davangere davangere ongoing 2 2
You are joining three separate tables. One is physical, tempproj, and the other two are virtual: they are aggregates.
This is the technique.
SELECT p.proj_name,p.Agency,p.District,p.Division,p.Proj_status,
Civilbills.billcount as Civilbills,
Electribills.billcount as Electricbills
FROM tempproj p
LEFT JOIN (
SELECT Proj_name, COUNT(*) as billcount
FROM payment80
where billtype='civil'
GROUP BY Proj_name
) Civilbills ON Civilbills.Proj_name = p.proj_name
LEFT JOIN (
SELECT Proj_name, COUNT(*) as billcount
FROM payment80
where billtype='Electric'
GROUP BY Proj_name
) Electricbills ON Electricbills.Proj_name = p.proj_name
Your requirement includes two separate aggregates from the payment80 table. The LEFT JOINs prevent suppression of project rows that lack any bills of either category.

MySQL Join Multiple (More than 2) Tables with Conditions

Assume I have 4 tables:
Table 1: Task
ID Task Schedule
1 Cut Grass Mon
2 Sweep Floor Fri
3 Wash Dishes Fri
Table 2: Assigned
ID TaskID (FK) PersonID (FK)
1 1 1
2 1 2
3 2 3
4 3 2
Table 3: Person
ID Name
1 Tom
2 Dick
3 Harry
Table 4: Mobile
ID PersonID (FK) CountryCode MobileNumber
1 1 1 555-555-5555
2 2 44 555-555-1234
3 3 81 555-555-5678
4 3 81 555-555-0000
I'm trying to display the
Task on a certain day
Name of person assigned to task
Phone numbers of said person
I think it should be something like the following, but I'm not sure how to set up the conditions so that the results are limited correctly:
SELECT T.ID, T.Task, P.Name, M.MobileNumber
FROM Task AS T
LEFT JOIN Assigned AS A
ON T.ID = A.TaskID
LEFT JOIN Person AS P
ON A.PersonID = P.ID
LEFT JOIN Mobile AS M
ON M.PersonID = P.ID
WHERE T.Schedule = Fri
My goal is to fetch the following information (it will be displayed differently):
Tasks Name MobileNumber
Sweep Floor, Wash Dishes Dick, Harry 44-555-555-1234, 81-555-555-5678, 81-555-555-0000
Of course, if JOIN is the wrong way to do this, please say so.
It's unclear what you want to do with duplicate data in this case, but you should be looking at using inner joins instead of outer joins, and using something like group_concat() to combine the phone numbers.