how to display multiple values, separated by commas, at single columns? - mysql

I am using mysql to perform queries. have the following 7 tables.
appointment //non-atomic, eg. values -> 1,7,3
gender //atomic value
module //non-atomic, eg. values -> 12,33
program
rank
staff
student
I have tried 'concat', 'find_in_set' & 'in' functions but cannot get it to work. How may I display multiple values # relations 'appointment' & 'module'?
The following statement is the closest i can get. Please let me know if additional details are required, thank you.
SELECT sta.staName
, r.rank
, sta.appointmentID
, a.appointment
, m.moduleCode
FROM staff AS sta
JOIN rank AS r
ON (sta.rankID = r.rankID)
JOIN appointment AS a
ON (sta.appointmentID = a.appointmentID)
JOIN module AS m
ON (sta.teachModuleID = m.moduleID)
WHERE sta.genderID = 1;

SELECT sta.staName, r.rank, sta.appointmentID,
group_concat(distinct a.appointment) as appointments,
group_concat(distinct m.moduleCode) as moduleCodes
FROM staff AS sta
INNER JOIN rank AS r ON sta.rankID = r.rankID
INNER JOIN appointment AS a ON find_in_set(sta.appointmentID, a.appointmentID) > 0
INNER JOIN module AS m ON find_in_set(sta.teachModuleID, m.moduleID) > 0
WHERE sta.genderID = 1
GROUP BY sta.staName, r.rank, sta.appointmentID

Related

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

How to write this SQL Statement to join 3 tables

I have 3 tables:-
Register(Stu_id, name, address,....,status),
Applicant_Choice(Stu_id,Sub_id1,Sub_id2,Sub_id3) for 3 selected subjects
Subjects(Sub_id,Subject_name,....) for subject names
I want to extract report in the form :- id,name, address,... 3 subject names for the Sub_id1,Sub_id2,Sub_id3 present in Applicant table where Register.status = 6.
So far I am using loops to generate the subject names for each record. But There will be 1000 records in Applicant table. So in that case so much queries to generate a pdf file will be too much!!! Can anybody show me how to write this query? Thanks in advance.
Try this one out:
select r.stu_id, r.name, r.address, s1.subject_name, s2.subject_name, s3.subject_name
from register r
inner join applicant_choice a
on r.stu_id = a.stu_id
inner join subjects s1
on a.sub_id1 = s1.sub_id
inner join subjects s2
on a.sub_id2 = s2.sub_id
inner join subjects s3
on a.sub_id3 = s3.sub_id
where r.status = 6;
Example fiddle: http://sqlfiddle.com/#!9/5daaa/3

How to filter on Access Report subtotal

Have an access report that shows training programs, and which employees should be but are not trained on that program. This query is fine. Problem is that we want to only display on the report training programs which have more than 10 employees untrained. So we have the total of untrained for each program in a subtotal, but we want to filter on that value.
How can this be done?
EDIT:
Here is pass-through query to SQL Server
SELECT T.ProgramTitle
,T.ProgramCode
,AE.Code AS 'AvantiCode'
,AE.FullName
,AE.FirstName
,AE.LastName
,AE.Department
,C.Position
,AE.Shift
FROM HR_Curriculum C
INNER JOIN HR_Trainings T ON C.TrainingID = T.TrainingID
INNER JOIN HR_EmployeeDetails ED ON C.Position = ED.Postion
INNER JOIN Avanti_Employees AE ON ED.AvantiRecID = AE.RecID
LEFT JOIN HR_Employeetrainings ET ON C.TrainingID = ET.TrainingID
AND ED.AvantiRecID = ET.AvantiRecID
LEFT JOIN HR_TrainingVersion V ON V.VersionID = ET.VersionID
WHERE terminated = 0
AND T.Active = - 1
AND CompletedDate IS NULL
GROUP BY T.ProgramTitle
,T.ProgramCode
,AE.Code
,AE.FullName
,AE.FirstName
,AE.LastName
,AE.Department
,C.Position
,AE.Shift
Order by programtitle
Consider an inline view, using a grouped by table alias with HAVING clause.
Try adding one more inner join:
INNER JOIN
(SELECT TrainingID, ProgramTitle, ProgramCode
FROM HR_Trainings
GROUP BY TrainingID, ProgramTitle, ProgramCode
HAVING Count(TrainingID) > 10) AS Trainings10More
ON Trainings10More.TrainingID = T.TrainingID

Using ALL with MySQL

I have three tables: Suppliers (id, name...), TypeOfServices(id, service...) and SupplierService(supplierId, ServiceId).
As search input, I have a list with type of service ids. I can have (1) or (1,3) or (1,2,3). I need a query in MySQL that will return to me all the Suppliers that provide the services that are in the search (it can provide other services as well, but it MUST provide the ones that are in the search).
I can't use IN, because IN(2,3) will return the suppliers that have serviceId 2 OR 3, and I need 2 AND 3.
I tried with ALL like this:
SELECT * FROM suppliers as s
INNER JOIN supplierservices as ss ON ss.SupplierId = s.Id
WHERE ss.ServiceId = ALL (SELECT id FROM typeofservices WHERE id IN (2, 3))
but it's not giving me any results and I have a supplier that provides both services (with id 2 and 3). I think that SQLServer accepts the query like this ALL(2,3), but in MySQL it's not working like that, you have to have a query inside ALL.
Try
SELECT * FROM suppliers as s
INNER JOIN supplierservices as ss ON ss.SupplierId = s.Id
WHERE ss.ServiceId IN(2,3)
GROUP BY s.id
HAVING COUNT (DISTINCT ServiceId)=2
You can do this as:
SELECT s.*
FROM suppliers s INNER JOIN
supplierservices ss
ON ss.SupplierId = s.Id
GROUP BY s.id
HAVING sum(id = 2) > 0 and
sum(id = 3) > 0;
Just add a new condition to the having clause for each value you need.

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'