GROUP_CONCAT not merging field - mysql

I have a syntax issue with my use of GROUP_CONTACT.
With a sql statement that looks like this:
SELECT
s.school_code
, s.school_name
,st.subject
,sg.subgroup
,GROUP_CONCAT(IF(r.year=2012,a.proficiency_index,NULL)) AS pi_2012
,GROUP_CONCAT(IF(r.year=2013,a.proficiency_index,NULL)) AS pi_2013
FROM
ayp_data a
INNER JOIN
report_year r ON
a.report_year_id = r.id
INNER JOIN
school s ON
a.school_code_id = s.id
INNER JOIN
sub_group sg ON
a.subgroup_id = sg.id
INNER JOIN
`subject` st ON
a.subject_id = st.id
GROUP BY
report_year_id,
s.school_code
, s.school_name
,st.subject
,sg.subgroup
HAVING
s.school_name = 'Moody Elementary School' AND
`subject` = 'Mathematics' AND
`subgroup` = 'All Students'
I am getting results like this:
SCHOOL_CODE SCHOOL_NAME SUBJECT SUBGROUP PI_2012 PI_2013
0065 Moody Elementary School Mathematics All Students 9.640000343322754 (null)
0065 Moody Elementary School Mathematics All Students (null) 10.920000076293945
I want to merge the two rows into one and put non-null field values PI_2012 and PI_2013 on the same line.
I thought I could do that with GROUP_CONTACT; but it's not doing as I thought it would.
How could I use GROUP_CONCAT to merge these fields?
Or, is there an even smarter way to do this?
I have the full schema and query here on SQL Fiddle.

If you don't want separate years to appear in separate rows, then you need to remove report_year_id from the GROUP BY clause. That is — you need to change this:
GROUP BY
report_year_id,
s.school_code
, s.school_name
,st.subject
,sg.subgroup
to this:
GROUP BY
s.school_code
, s.school_name
,st.subject
,sg.subgroup

Related

Select query with MySql left join

I have 3 table (property, facility, property_facility)
Now I want to get all facilities from facility table alone with property data to given property ID.
I tried it with LEFT JOIN as below. But I cannot get all facilities from facility table.
SELECT property_id
, contract_id
, type_id
, location_id
, beds
, ROUND(price,3) as price
, f.facility_id
, f.name
, pf.facility_id
FROM facility f
LEFT JOIN property_facility pf ON pf.facility_id = f.facility_id AND pf.property_id = 6
LEFT JOIN property p USING(property_id)
WHERE p.property_id = 6
Can anybody tell me how I make this query correctly?
Your WHERE clause is filtering out the facilities that do not match. You will need to switch the USING to ON and do:
FROM facility f LEFT JOIN
property_facility pf
ON pf.facility_id = f.facility_id AND
pf.property_id = 6 LEFT JOIN
property p
ON p.property_id = pf.property_id AND p.property_id = 6
You seem to understand the concept, because you are using the same condition for the first JOIN.
Well, actually, the property_id doesn't change. So, you can just remove the WHERE clause and continue with USING if you prefer.

SQL: SELECT where 2 columns from different tables are the same

I need to work with a database that contains info about (former) Presidents. I need to check if there a presidents that have the same hobbies AND are married in the same year.
So a president can have multiple hobbies in pres_hob table. And the marriage year is in the pres_mar table, in the mar_year column.
I've tried to INNER JOIN the tables in SQLite where the hobby and mar_year are equal, except for the pres_name. This way the JOIN doesnt work ofcourse, which makes sense. Im kinda new to this..
Any help is appreciated
Here's one option with multiple joins:
select p1.pres_name, p2.pres_name, ph.hobby
from pres_mar p1
join pres_mar p2 on p1.pres_name != p2.pres_name and p1.mar_year = p2.mar_year
join pres_hob ph on p1.pres_name = ph.pres_name
join pres_hob ph2 on p2.pres_name = ph2.pres_name and ph.hobby = ph2.hobby
And depending on your expected results, another option using exists:
select pm.pres_name, ph.hobby
from pres_mar pm
join pres_hob ph on pm.pres_name = ph.pres_name
where exists (
select 1
from pres_mar pm2
join pres_hob ph2 on pm2.pres_name = ph2.pres_name
where pm.pres_name != pm2.pres_name and
ph.hobby = ph2.hobby
)
That sounds like a terrible database schema, I'm assuming its for learning purposes, anyway, you could do something like
SELECT
h.name,
h.hobby,
m.year
FROM
pres_hob h,
pres_mar m
WHERE
h.hobby = 'tennis'
AND
m.year = 2016
This would return 1 record for every president with a marriage year of 2016 , and a hobby of Tennis.

Multiple answers on one line

I have the following tables:
matters(matterid, mattername, refno)
mattersjuncstaff(junked, matterid, staffid, lead)
staff(staffid, staffname)
A matter may have a number of staff associated with it and a number of those staff will be marked as ‘leads’ i.e. they will have a ‘Y’ in the ‘lead’ field.
I wish to show a table that has a list of matters, the matter name and ref no and those staff marked as leads, ideally in a single row. So it would look something like:
reference | mattername | Lead Staff |
ABC1 | matter abc & Co | Fred Smith, Jane Doe, Naomi Watts |
etc
I am using the code below but this only displays one person with the lead field marked Y.
SELECT refno, mattername, matters.matterid, staffname
FROM matters
INNER JOIN matterjuncstaff
USING (matterid)
Inner join staff
using (staffid)
Inner join matterjuncactions
On matterjuncactions.matterid = matters.matterid
WHERE lead = 'Y'
GROUP BY matters.matterid, nickname
Can anyone tell me how I can I get round this?
You want to concatenate values from a join and represent that as a field in the result set. GROUP_CONCAT function is suited for such queries:
SELECT m.matterid, m.refno, m.mattername, GROUP_CONCAT(s.staffname) AS LeadStaff
FROM matters m
LEFT JOIN matterjuncstaff mjs ON mjs.matterid = m.matterid AND lead = 'Y'
LEFT JOIN staff s ON s.staffid = mjs.staffid
GROUP BY m.matterid, m.refno, m.mattername
The join changed to LEFT and lead = 'Y' moved there, otherwise you will lose matters with no lead staffs.
Use INNER JOIN if you only want matters having some lead staff.
I have removed matterjuncactions as you did not give its info.
Use the GROUP_CONCAT() function in mysql to concatenate values from a query into a single string.
For example you could select a row for each matter and append a column with all the concatenated lead staff names as follows:
SELECT m.refno,
m.mattername,
(Select GROUP_CONCAT(distinct staffname SEPARATOR ', ')
from mattersjuncstaff js
join staff s
on s.staffid = js.staffid
where js.lead = 'Y'
and js.matterid = m.matterid) as LeadStaffMembers
FROM matters m
Update
Here is the same example, but with an added column showing staff members that are not the lead.
SELECT m.refno,
m.mattername,
(Select GROUP_CONCAT(distinct staffname SEPARATOR ', ')
from mattersjuncstaff js
join staff s
on s.staffid = js.staffid
where js.lead = 'Y'
and js.matterid = m.matterid) as LeadStaffMembers,
(Select GROUP_CONCAT(distinct staffname SEPARATOR ', ')
from mattersjuncstaff js
join staff s
on s.staffid = js.staffid
where js.lead <> 'Y'
and js.matterid = m.matterid) as NonLeadStaffMembers
FROM matters m

SQL: Get latest entries from history table

I have 3 tables
person (id, name)
area (id, number)
history (id, person_id, area_id, type, datetime)
In this tables I store the info which person had which area at a specific time. It is like a salesman travels in an area for a while and then he gets another area. He can also have multiple areas at a time.
history type = 'I' for CheckIn or 'O' for Checkout.
Example:
id person_id area_id type datetime
1 2 5 'O' '2011-12-01'
2 2 5 'I' '2011-12-31'
A person started traveling in area 5 at 2011-12-01 and gave it back on 2011-12-31.
Now I want to have a list of all the areas all persons have right now.
person1.name, area1.number, area2.number, area6.name
person2.name, area5.number, area9.number
....
The output could be like this too (it doesn't matter):
person1.name, area1.number
person1.name, area2.number
person1.name, area6.number
person2.name, area5.number
....
How can I do that?
This question is, indeed, quite tricky. You need a list of the entries in history where, for a given user and area, there is an 'O' record with no subsequent 'I' record. Working with just the history table, that translates to:
SELECT ho.person_id, ho.area_id, ho.type, MAX(ho.datetime)
FROM History AS ho
WHERE ho.type = 'O'
AND NOT EXISTS(SELECT *
FROM History AS hi
WHERE hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
)
GROUP BY ho.person_id, ho.area_id, ho.type;
Then, since you're really only after the person's name and the area's number (though why the area number can't be the same as its ID I am not sure), you need to adapt slightly, joining with the extra two tables:
SELECT p.name, a.number
FROM History AS ho
JOIN Person AS p ON ho.person_id = p.id
JOIN Area AS a ON ho.area_id = a.id
WHERE ho.type = 'O'
AND NOT EXISTS(SELECT *
FROM History AS hi
WHERE hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
);
The NOT EXISTS clause is a correlated sub-query; that tends to be inefficient. You might be able to recast it as a LEFT OUTER JOIN with appropriate join and filter conditions:
SELECT p.name, a.number
FROM History AS ho
JOIN Person AS p ON ho.person_id = p.id
JOIN Area AS a ON ho.area_id = a.id
LEFT OUTER JOIN History AS hi
ON hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
WHERE ho.type = 'O'
AND hi.person_id IS NULL;
All SQL unverified.
You're looking for results where each row may have a different number of columns? I think you may want to look into GROUP_CONCAT()
SELECT p.`id`, GROUP_CONCAT(a.`number`, ',') AS `areas` FROM `person` a LEFT JOIN `history` h ON h.`person_id` = p.`id` LEFT JOIN `area` a ON a.`id` = h.`area_id`
I haven't tested this query, but I have used group concat in similar ways before. Naturally, you will want to tailor this to fit your needs. Of course, group concat will return a string so it will require post processing to use the data.
EDIT I thikn your question has been edited since I began responding. My query does not really fit your request anymore...
Try this:
select *
from person p
inner join history h on h.person_id = p.id
left outer join history h2 on h2.person_id = p.id and h2.area_id = h.area_id and h2.type = 'O'
inner join areas on a.id = h.area_id
where h2.person_id is null and h.type = 'I'

MySQL - Using column value for joining in the same query

I have three tables that looks something like this:
Table joins
|ID|JOIN_NAME|
1 persons
2 companies
Table information
|ID|JOIN_ID|
1 1
2 2
Table information_extra_persons
|ID|INFORMATION_ID|NAME|
1 1 John
Table information_extra_companies
|ID|INFORMATION_ID|NAME|
1 2 IBM
How can i join together these tables in one SQL? I've tried something like:
SELECT * FROM `information`
INNER JOIN `information_extra_(SELECT `name` FROM `joins` WHERE `id` = `join_id`)`
ON `information_extra_(SELECT `name` FROM `joins` WHERE `id` = `join_id`)`.`information_id` = `information`.`id`
but I can't get it to work. Of course this isn't my actual table setup, but it's the same principle. Does anyone know how to get all the info in just one SQL?
That's actually four tables, not three. This isn't just a nitpick - it looks as though the substance of your question is "how can I use the name of the table as part of the join criteria?" (ie. how can the information_extra_ tables be treated as a single table?)
To which the answer is: you can't. (Outside of dynamic SQL.)
In this specific case, the following should return what I think you are looking for:
select j.join_name joined_entity,
case when j.join_name = 'persons' then p.name
else c.name
end joined_entity_name
from information i
inner join joins j on i.join_id = j.id
left join information_extra_persons p on i.id = p.information_id
left join information_extra_companies c on i.id = c.information_id
Alternatively, a less efficient (but more general) approach might be:
select j.join_name joined_entity,
v.name joined_entity_name
from information i
inner join joins j on i.join_id = j.id
inner join (select 'persons' entity, information_id, name from information_extra_persons
union all
select 'companies' entity, information_id, name from information_extra_companies) v
on i.id = v.information_id and j.join_name = v.entity