Using GROUP_CONCAT on multiple fields - mysql

Here's a picture of my database structure:
With help from users on here I've managed to put together quite a complex SQL statement using GROUP_CONCAT:
SELECT
t1.Name AS Teacher_Name,
t2.Name AS Observer_Name,
o.Datetime AS Datetime,
o.Type AS Type,
o.Year_Group AS Year_Group,
o.Class_Name AS Class_Name,
c.Title AS Course_Name,
GROUP_CONCAT(l.Title) AS Focus,
o.Achievement_Grade AS Achievement_Grade,
o.Behaviour_Grade AS Behaviour_Grade,
o.Teaching_Grade AS Teaching_Grade,
GROUP_CONCAT(cl1.Title) AS Positive,
GROUP_CONCAT(cl2.title) AS Development,
o.Notes AS Notes
FROM observations o
LEFT JOIN teachers t1
ON o.Teacher_ID = t1.Teacher_ID
LEFT JOIN teachers t2
ON o.Observer_ID = t2.Teacher_ID
LEFT JOIN courses c
ON o.Course_ID = c.Course_ID
LEFT JOIN foci f
ON o.ID = f.Observation_ID
LEFT JOIN focus_labels l
on f.focus_id = l.id
LEFT JOIN criteria c1
ON o.ID = c1.Observation_ID
LEFT JOIN criteria_labels cl1
on c1.Criteria_ID = cl1.ID AND c1.Type = 'P'
LEFT JOIN criteria c2
ON o.ID = c2.Observation_ID AND c2.Type = 'D'
LEFT JOIN criteria_labels cl2
on c2.Criteria_ID = cl2.ID
GROUP BY o.id
ORDER BY `Datetime` DESC";
This appears to work OK, apart from the fact that Focus, Positive and Development are each repeated depending on the field that has the highest number of concatenations in.
For example, if Positive has Pace,Progress,Attainment but Focus is only Appraisal, it'll be repeated three times (Appraisal,Appraisal,Appraisal).
I've looked this up and I think it could be because I need to GROUP each of these GROUP_CONCAT JOINs. However, I have no idea how to go about this.
Can anyone help? Thanks in advance,

GROUP_CONCAT has DISTINCT attribute that can be applied to remove duplicates.
SELECT
t1.Name AS Teacher_Name,
t2.Name AS Observer_Name,
o.Datetime AS Datetime,
o.Type AS Type,
o.Year_Group AS Year_Group,
o.Class_Name AS Class_Name,
c.Title AS Course_Name,
GROUP_CONCAT(DISTINCT l.Title) AS Focus,
o.Achievement_Grade AS Achievement_Grade,
o.Behaviour_Grade AS Behaviour_Grade,
o.Teaching_Grade AS Teaching_Grade,
GROUP_CONCAT(cl1.Title) AS Positive,
GROUP_CONCAT(cl2.title) AS Development,
o.Notes AS Notes
FROM observations o
LEFT JOIN teachers t1
ON o.Teacher_ID = t1.Teacher_ID
LEFT JOIN teachers t2
ON o.Observer_ID = t2.Teacher_ID
LEFT JOIN courses c
ON o.Course_ID = c.Course_ID
LEFT JOIN foci f
ON o.ID = f.Observation_ID
LEFT JOIN focus_labels l
on f.focus_id = l.id
LEFT JOIN criteria c1
ON o.ID = c1.Observation_ID
LEFT JOIN criteria_labels cl1
on c1.Criteria_ID = cl1.ID AND c1.Type = 'P'
LEFT JOIN criteria c2
ON o.ID = c2.Observation_ID AND c2.Type = 'D'
LEFT JOIN criteria_labels cl2
on c2.Criteria_ID = cl2.ID
GROUP BY o.id
ORDER BY `Datetime` DESC";

Related

Convert query with exists to joins

I want to convert this query in such a way so it does not have any 'exists' and uses only simple joins.
select t.Teacher_id, t.Teacher_name, a.marks, a.grade_ID
from Grades a
left join students s on a.student_ID = s.student_ID
left join Teachers t on t.Teacher_ID = s.Teacher_ID
where 1=1 and t.Teacher_id = 1807600
and exists(
select p.Payment_ID from payments p
inner join lookups l on (l.lookup_id = p.status_id and l.lookup_key in ('condition1','condition2'))
where p.student_ID = a.student_ID
)
I tried something like:
select t.Teacher_id, t.Teacher_name, a.marks, a.grade_ID
from Grades a
left join students s on a.student_ID = s.student_ID
left join Teachers t on t.Teacher_ID = s.Teacher_ID
inner join payments p on p.student_ID = a.student_ID
inner join lookups l on (l.lookup_id = p.status_id and l.lookup_key in ('condition1','condition2'))
where 1=1 and t.Teacher_id = 1807600
But I'm not getting the right results. Can you please help. Thanks.
I suppose you could use group by, but this restricts the maintainability of the script for the simple sake of not using EXISTS().
select t.Teacher_id, t.Teacher_name, a.marks, a.grade_ID
from Grades a
left join students s on a.student_ID = s.student_ID
left join Teachers t on t.Teacher_ID = s.Teacher_ID
inner join payments p on p.student_ID = a.student_ID
inner join lookups l on (l.lookup_id = p.status_id and l.lookup_key in ('condition1','condition2'))
where t.Teacher_id = 1807600
group by t.Teacher_id, t.Teacher_name, a.marks, a.grade_ID

Problems with reusing LEFT JOIN results in WHERE and ORDER BY Clause

SELECT s.*,
u.username,
u.fullname,
c.title AS ctitle,
c.description AS cdescription,
sa.attention,
sp.popularity,
COUNT(DISTINCT f.id) AS favorites,
COUNT(DISTINCT st.id) AS stars,
COUNT(DISTINCT v.id) AS views
FROM shots s
INNER JOIN users u ON u.id = s.user_id
INNER JOIN categories c ON c.id = s.cat_id
LEFT OUTER JOIN(
SELECT shot_id, round(AVG(count),2) AS attention
FROM points
WHERE date > DATE_SUB(CURDATE(),INTERVAL 2 DAY)
GROUP BY shot_id
) sa ON sa.shot_id = s.id
LEFT OUTER JOIN(
SELECT shot_id, SUM(count) AS popularity
FROM points
GROUP BY shot_id
) sp ON sp.shot_id = s.id
LEFT OUTER JOIN favorites f ON f.shot_id = s.id
LEFT OUTER JOIN stars st ON st.shot_id = s.id
LEFT OUTER JOIN views v ON v.shot_id = s.id
**WHERE s.library = 1 AND sa.attention > 40
ORDER BY sa.attention DESC
LIMIT 0,50**
GROUP BY s.id
I can't use the sa.attention in a condition and for ordering. Why?
(I removed the marked part, and the query works!)
What do I have to change in my Query? And if you could give a explanation for it, that would be very nice!
You are negating your OUTER JOIN by putting that in your WHERE criteria. Move it to your JOIN and you'll get your NULL records back:
SELECT s.*,
u.username,
u.fullname,
c.title AS ctitle,
c.description AS cdescription,
sa.attention,
sp.popularity,
COUNT(DISTINCT f.id) AS favorites,
COUNT(DISTINCT st.id) AS stars,
COUNT(DISTINCT v.id) AS views
FROM shots s
INNER JOIN users u ON u.id = s.user_id
INNER JOIN categories c ON c.id = s.cat_id
LEFT OUTER JOIN(
SELECT shot_id, round(AVG(count),2) AS attention
FROM points
WHERE date > DATE_SUB(CURDATE(),INTERVAL 2 DAY)
GROUP BY shot_id
) sa ON sa.shot_id = s.id AND sa.attention > 40
LEFT OUTER JOIN(
SELECT shot_id, SUM(count) AS popularity
FROM points
GROUP BY shot_id
) sp ON sp.shot_id = s.id
LEFT OUTER JOIN favorites f ON f.shot_id = s.id
LEFT OUTER JOIN stars st ON st.shot_id = s.id
LEFT OUTER JOIN views v ON v.shot_id = s.id
WHERE s.library = 1
GROUP BY s.id
ORDER BY sa.attention DESC
LIMIT 0,50
A second note, GROUP BY cannot go at the end. I moved that to the correct spot as well.

Rows missing from mysql pivot query results

I have a mysql query as stated below, it returns exactly the results I want for one row, but doesn't return any other rows where I expect there to be 8 in my test data (there are 8 unique test ids). I was inspired by this answer but obviously messed up my implementation, does anyone see where I'm going wrong?
SELECT
c.first_name,
c.last_name,
n.test_name,
e.doc_name,
e.email,
e.lab_id,
a.test_id,
a.date_req,
a.date_approved,
a.accepts_terms,
a.res_value,
a.reason,
a.test_type,
a.date_collected,
a.date_received,
k.kind_name,
sum(case when metabolite_name = "Creatinine" then t.res_val end) as Creatinine,
sum(case when metabolite_name = "Glucose" then t.res_val end) as Glucose,
sum(case when metabolite_name = "pH" then t.res_val end) as pH
FROM test_requisitions AS a
INNER JOIN personal_info AS c ON (a.user_id = c.user_id)
INNER JOIN test_types AS d ON (a.test_type = d.test_type)
INNER JOIN kinds AS k ON (k.id = d.kind_id)
INNER JOIN test_names AS n ON (d.name_id = n.id)
INNER JOIN docs AS e ON (a.doc_id = e.id)
INNER JOIN test_metabolites AS t ON (t.test_id = a.test_id)
RIGHT JOIN metabolites AS m ON (m.id = t.metabolite_id)
GROUP BY a.test_id
ORDER BY (a.date_approved IS NOT NULL),(a.res_value IS NOT NULL), a.date_req, c.last_name ASC;
Most of your joins are inner joins. The last is a right outer join. As written, the query keeps all the metabolites, but not necessarily all the tests.
I would suggest that you change them all to left outer joins, because you want to keep all the rows in the first table:
FROM test_requisitions AS a
LEFT JOIN personal_info AS c ON (a.user_id = c.user_id)
LEFT JOIN test_types AS d ON (a.test_type = d.test_type)
LEFT JOIN kinds AS k ON (k.id = d.kind_id)
LEFT JOIN test_names AS n ON (d.name_id = n.id)
LEFT JOIN docs AS e ON (a.doc_id = e.id)
LEFT JOIN test_metabolites AS t ON (t.test_id = a.test_id)
LEFT JOIN metabolites AS m ON (m.id = t.metabolite_id)
I would also suggest that your aliases be related to the table, so tr for test_requisition, pi for personal_info, and so on.

Amend JOIN to filter results by list of IDs in foreign table

Here's my database structure:
The SQL statement I'm using is:
SELECT
t1.Name AS Teacher_Name,
t2.Name AS Observer_Name,
o.Datetime AS Datetime,
o.Type AS Type,
o.Year_Group AS Year_Group,
o.Class_Name AS Class_Name,
c.Title AS Course_Name,
GROUP_CONCAT(DISTINCT l.Title SEPARATOR ', ') AS Focus,
o.Achievement_Grade AS Achievement_Grade,
o.Behaviour_Grade AS Behaviour_Grade,
o.Teaching_Grade AS Teaching_Grade,
GROUP_CONCAT(DISTINCT cl1.Title SEPARATOR ', ') AS Positive,
GROUP_CONCAT(DISTINCT cl2.title SEPARATOR ', ') AS Development,
o.Notes AS Notes
FROM observations o
LEFT JOIN teachers t1
ON o.Teacher_ID = t1.Teacher_ID
LEFT JOIN teachers t2
ON o.Observer_ID = t2.Teacher_ID
LEFT JOIN courses c
ON o.Course_ID = c.Course_ID
LEFT JOIN foci f
ON o.ID = f.Observation_ID
LEFT JOIN focus_labels l
on f.focus_id = l.id
LEFT JOIN criteria c1
ON o.ID = c1.Observation_ID
LEFT JOIN criteria_labels cl1
on c1.Criteria_ID = cl1.ID AND c1.Type = 'P'
LEFT JOIN criteria c2
ON o.ID = c2.Observation_ID AND c2.Type = 'D'
LEFT JOIN criteria_labels cl2
on c2.Criteria_ID = cl2.ID
GROUP BY o.id
ORDER BY Datetime DESC
The data that's returned from such a resulset is:
What I'm trying to achieve now is a search by Course.
The input I have for this is a list of IDs, so I was hoping I could use something like:
SELECT * FROM Courses WHERE Course_ID IN (1, 2, 3, 4).
Obviously it isn't that straightforward though, as my Courses are on a JOIN:
LEFT JOIN courses c ON o.Course_ID = c.Course_ID.
How would I go about amending this statement to include such search functionality?
Thanks in advance,
Why not? Just add to your query where statement:
.............................
LEFT JOIN criteria_labels cl2
on c2.Criteria_ID = cl2.ID
WHERE o.Course_ID IN (1, 2, 3, 4)
GROUP BY o.id
ORDER BY Datetime DESC

only returning records when s.id = u.summary_id

select
s.id, s.description, s.improvement, s.previous_year_id,
s.current_year_id, s.first_name, s.last_name, s.username,
s.finding, s.action, s.share, s.learned, s.timestamp,
d.title as department_title,
group_concat(g.title SEPARATOR \' | \') as strategic_goals,
y1.year as current_year_title, y2.year as previous_year_title,
u.summary_id, u.file_name as file_name
from
summary s, year y1, year y2, strategic_goal_entries sge,
goal g, department d, uploads u
where
s.id = sge.summary_id
and
s.current_year_id = y1.id
and
s.previous_year_id = y2.id
and
sge.goal_id = g.id
and
s.id = u.summary_id
and
s.department_id = d.id
and
s.department_id = '4'
group by
s.id
This only returns records from the summary table that has a relating record in the uploads table (s.id = uploads.summary_id) that contain a value within the uploads.summary_id field
I want to return all records, whether or not it has a file associated with it.
Any help is appreciated.
Suggest refactoring this SQL query to use ANSI joins. To achive your goal, you'd want a LEFT JOIN instead:
SELECT /*your columns*/
from summary s
INNER JOIN year y1 ON s.current_year_id = y1.id
INNER JOIN year y2 ON s.previous_year_id = y2.id
INNER JOIN strategic_goal_entries sge ON s.id = sge.summary_id
INNER JOIN goal g ON sge.goal_id = g.id
INNER JOIN department d ON s.department_id = d.id
LEFT JOIN uploads u ON s.id = u.summary_id
WHERE s.department_id = '4'
group by s.id