SQL/MySQL Query Assistance - mysql

I need some help with this SQL Query. It is designed to retrieve names of students with the same S.S_level values as Jaci Walker, and have taken courses (CS.C_SE_id) with Jaci Walker in the BG building.
I am having trouble on line 7. I need to be able to ensure that the people have enrolled in the same course as Jaci Walker. I'm not sure about what to put in the WHERE statement for that section.
The database schema can be seen here:
SELECT S.S_Fname, S.S_LName
FROM Student S, Enrollment E, CourseSection CS, Location L
WHERE S.S_id = E.S_id
AND E.C_SE_ID = CS.C_SE_id
AND L.Loc_id = CS.Loc_ID
AND S.S_Level = (SELECT S.S_Level FROM Student S WHERE S.S_Fname = "Jaci" AND S.S_Lname = "Walker")
AND CS.C_SE_id = (SELECT CS.C_SE_id FROM CourseSection CS WHERE **?**)
AND L.Loc_id = (SELECT L.Blodg_code FROM Location L WHERE L.Blodg_code = "BG");

I would start by using current SQL-syntax using JOIN conditions instead of using the WHERE clause to show relationships between tables. This way, you get all your table associations done and can better visually confirm you have those elements configured... THEN, tack on the criteria you are looking for.
What I have done here is to just have a PreQuery (result alias "JaciClassesInBG" ) that gets all of Jaci's classes that were enrolled in and ONLY those for the building "BG" (which was added to the JOIN clause to the location table). The WHERE clause was only for Jaci.
From that result, I have a list of all classes that Jaci took. I grabbed her ID, S_Level and C_SE_ID entries.
From that, just join back to the enrollment table of all other students based explicitly on the C_SE_ID that Jaci took (thus all students in that exact same class). However, I've EXCLUDED (via AND NOT...) Jaci's student ID from the list... we know she took the class, we are looking for everyone ELSE.
Finally, join that result back to the students table based on the common enrollment. Now, we can associate the common "S_LEVEL" criteria of Jaci to those students...
Now, you can get whatever details you want for display... in this case, I am grabbing each student, and what class they had in common with Jaci. One student may have been in multiple classes. This will show each. If you only care about one instance, I would just change the top to...
select DISTINCT S2.S_FName, S2.S_LName...
SELECT
JaciClassesInBG.Course_Code,
JaciClassesInBG.Course_Name,
S2.S_FName,
S2.S_LName
from
( SELECT
S.ID,
S.S_Level,
CS.C_SE_ID,
C.Course_Code,
C.Course_Name
FROM
Student S
JOIN Enrollment E
ON S.S_id = E.S_id
JOIN CourseSection CS
ON E.C_SE_ID = CS.C_SE_id
JOIN Location L
ON L.Loc_id = CS.Loc_ID
AND L.Blodg_Code = "BG"
JOIN Course C
ON CS.Course_ID = C.Course_ID
WHERE
S.S_Fname = "Jaci"
AND S.S_Lname = "Walker" ) JaciClassesInBG
JOIN
Enrollment E2
ON JaciClassesInBG.C_SE_ID = E2.C_SE_ID
AND NOT JaciClassesInBG.S_ID = E2.S_ID
JOIN Students S2
ON E2.S_ID = S2.S_ID
AND JaciClassesInBG.S_Level = S2.S_Level

IF I'm understanding the question right this should work:
SELECT s.S_Fname, s.S_LName
FROM Student s
INNER JOIN Enrollment e ON s.S_id = e.S_id
INNER JOIN CourseSection cs ON e.C_SE_ID = cs.C_SE_id
INNER JOIN Location l ON cs.Loc_ID = l.Loc_id
INNER JOIN Student s2 ON s.S_Level = s2.S_Level AND s2.S_Fname = "Jaci" AND s2.S_Lname = "Walker"
INNER JOIN Enrollment e2 ON s2.S_id = e2.S_id
INNER JOIN CourseSection cs2 ON e2.C_SE_ID = cs2.C_SE_id
WHERE l.Loc_id = l.Blodg_code = "BG"
AND cs.Course_ID = cs2.Course_ID
I'm unable to test at the moment and it was quickly done. Maybe a better solution can be found?

Without access to the data I can't test but the following may help (the first inner select is pulling back all the relevant C_SE_ID's)
SELECT S.S_Fname, S.S_Lname
FROM Student S, Enrollment E
WHERE S.S_id = E.S_id
AND E.C_SE_ID IN (SELECT CS.C_SE_ID
FROM Student S2, Enrollment E2, CourseSection CS, Location L
WHERE S2.S_id = E2.S_id
AND E2.C_SE_ID = CS.C_SE_ID
AND CS.Loc_id = L.Loc_id
AND L.Blodg_code = 'BG'
AND S2.S_Fname = 'Jaci'
AND S2.S_Lname = 'Walker')
AND S.S_Level = (SELECT S3.S_Level
FROM Student S3
WHERE S3.S_Fname = 'Jaci'
AND S3.S_Lname = 'Walker')
AND S.S_Fname <> 'Jaci'
AND S.S_Lname <> 'Walker'
I would recommend using different aliases within inner queries (e.g. S2, E2) to avoid confusion.

Related

Mysql subqueries problem with data retrieved

I'm trying to run a query but my outcome is not what i need.
So the problem is:
A user can be diretor and if this is the case he can see all activities from his department, and he can be user of another department too, in this case not director but only user.
I have 8 departments each with one director, so the following query should give me the activities of the department and the activities of this particular user in other department:
SELECT t1.idAtividade,
t1.idProfessor,
t2.Escola,
t1.Atividade,
t1.Periodo,
t1.Mes,
t1.haveClasses,
t1.DataPrevista,
t1.Destinatarios,
t1.Orcamento,
t1.PdfAtividade,
t1.Avaliacao,
t1.PdfAvaliacao,
t1.idProfessor,
p.Nome,
g.Grupo,
d.Departamento,
p2.Projeto,
t1.idProjeto
FROM atividades AS t1
INNER JOIN professores p on t1.idProfessor = p.idProfessor
INNER JOIN atividadesgrupos ag on t1.idAtividade = ag.idAtividade
INNER JOIN grupos g on ag.idGrupo = g.idGrupo
INNER JOIN departamentosatividades da on t1.idAtividade = da.idAtividade
INNER JOIN departamentos d on da.idDepartamento = d.idDepartamento
INNER JOIN escolas AS t2 ON (t2.idEscola = t1.idEscola)
INNER JOIN anosescolares AS ae ON (t1.idAnoEscolar = ae.idAnoEscolar)
INNER JOIN projetos p2 on t1.idProjeto = p2.idProjeto
WHERE ae.Estado = 1 AND (da.idDepartamento = :id_dpt and ag.idGrupo = :idGrupo)
ORDER BY (t1.idProfessor = :idProfessor) DESC, t1.idProfessor;");
This query is not working because the department have 22 activities but this user (idProfessor) has 5 in this department and 14 more in another department (defined by idGrupo)
I think i will need a subquery right?

SQL - return rows that do not have a certain value

looking for a bit of help here if possible?
I have the following query:-
On or database we have a table called Linkfile, in this table are "Types" all beginning with "YG". I need to return those rows that do not have the type of "YG8" but just cannot seem to do it. I know ill need to use a sub query but am stuck!
This is my code and the fields I need to return. I just need to only show those that do not have the lk.type of "YG8"
select distinct l.description, p.displayname AS Temp, p.compliance_status As 'Compliant', lk.displayname, lk.type
from event e
inner join organisation o on e.organisation_ref = o.organisation_ref
inner join opportunity opp on e.opportunity_ref = opp.opportunity_ref
inner join event_role ev on ev.event_ref = e.event_ref
inner join address a on a.address_ref = opp.address_ref
inner join person p on ev.person_ref = p.person_ref
inner join lookup l on p.responsible_team = l.code
inner join person_type pt on p.person_ref = pt.person_ref
inner join linkfile lk on lk.parent_object_ref = pt.person_ref
where o.displayname LIKE '%G4S%' and p.compliance_category = '$016'
and lk.type like 'YG%' and l.code_type = '2'
and a.displayname LIKE '%MOJ%'
and pt.status = 'A'
order by l.description, p.displayname, lk.type
Use below query :
select distinct l.description, p.displayname AS Temp, p.compliance_status As 'Compliant', lk.displayname, lk.type,lk.parent_object_ref
from event e
inner join organisation o on e.organisation_ref = o.organisation_ref
inner join opportunity opp on e.opportunity_ref = opp.opportunity_ref
inner join event_role ev on ev.event_ref = e.event_ref
inner join address a on a.address_ref = opp.address_ref
inner join person p on ev.person_ref = p.person_ref
inner join lookup l on p.responsible_team = l.code
inner join person_type pt on p.person_ref = pt.person_ref
left join (select displayname, type,parent_object_ref from linkfile where lk.type like 'YG8%' )lk on lk.parent_object_ref = pt.person_ref
where o.displayname LIKE '%G4S%' and p.compliance_category = '$016' and lk.parent_object_ref is null
and l.code_type = '2'
and a.displayname LIKE '%MOJ%'
and pt.status = 'A'
order by l.description, p.displayname, lk.type;
I've used left join on linkfile with type like 'YG8%' and fetching the only records which are not matched
I think you can just replace the
lk.type like 'YG%'
with the following:
(lk.type >= 'YG' and lk.type <'YG8') or (lk.type > 'YG8' and lk.type <='YGZ')
this should accomplish what you are trying to do and also avoid using "like" which is less efficient (assuming you have an index on lk.type, at least).
You may refine this a bit by knowing which are the possible values of lk.type of course. I.e. what are the extremes for the YG "subtype"? YG00-YG99? YG-YGZ?
(Be especially careful if you may have YG81 or YG87 for example, because then my clause will not work properly... on the other hand if your YG subtype can have values like YG34 it would have been better to use YG08 instead of YG8)

How to use same field from a table twice in one query

I have two tables: calls and employees. In the calls tables I have a field enter_emp_id and another follow_emp_id. They hold the values for which employee entered the call and which employee is assigned to the call. My second table employees has employee_id and employee fields. I am able to write a query to show results with an employee name for who entered the call, but not who it is assigned to. I do have other values in the query, but I need to get the 2nd employee name to show in the results. Here is what I am using so far:
SELECT
`calls`.`call_id`,
`calls`.`enter_date`,
`call_state`.`call_state`,
`customers`.`customer_name`,
`calls`.`comments`,
`employees`.`employee`,
`call_reasons`.`call_reason`
FROM
`customers`
INNER JOIN `calls` ON (`customers`.`customer_id` = `calls`.`customer_id`)
INNER JOIN `employees` ON (`employees`.`employee_id` = `calls`.`enter_emp_id`)
INNER JOIN `call_state` ON (`call_state`.`call_state_id` = `calls`.`call_state_id`)
INNER JOIN `call_reasons` ON (`call_reasons`.`call_reason_id` = `calls`.`call_reason_id`)
WHERE
`calls`.`call_id` = $call_id;
You need to join to the employee table TWICE... once for each employee id association, and use the ALIAS of the respective to get the values intended. And aliasing the tables and removal of unnecessary tick marks not required. Only needed for possible reserved word conflicts.
SELECT
c.call_id,
c.enter_date,
cs.call_state,
cust.customer_name,
c.comments,
entered.employee,
assigned.employee as AssignedEmployee,
cr.call_reason
FROM
calls c
INNER JOIN customers cust
ON c.customer_id = cust.customer_id
INNER JOIN employees entered
ON c.enter_emp_id = entered.employee_id
INNER JOIN employees assigned
ON c.enter_emp_id = assigned.employee_id
INNER JOIN call_state cs
ON c.call_state_id = cs.call_state_id
INNER JOIN call_reasons cr
ON c.call_reason_id = cr.call_reason_id
WHERE
c.call_id = $call_id;
I hope i understand you problem. You can use AS to change or shorten table name for sql usage
SELECT
`calls`.`call_id`,
`calls`.`enter_date`,
`call_state`.`call_state`,
`customers`.`customer_name`,
`calls`.`comments`,
`employees`.`employee`,
`assigned`.`employee` as assigned_employee,
`call_reasons`.`call_reason`
FROM
`customers`
INNER JOIN `calls` ON (`customers`.`customer_id` = `calls`.`customer_id`)
INNER JOIN `employees` ON (`employees`.`employee_id` = `calls`.`enter_emp_id`)
INNER JOIN `employees` AS `assigned` ON (`assigned`.`employee_id` = `calls`.`follow_emp_id`)
INNER JOIN `call_state` ON (`call_state`.`call_state_id` = `calls`.`call_state_id`)
INNER JOIN `call_reasons` ON (`call_reasons`.`call_reason_id` = `calls`.`call_reason_id`)
WHERE
`calls`.`call_id` = $call_id;
Thanks the following worked:
SELECT
c.call_id,
c.enter_date,
cs.call_state,
cust.customer_name,
c.comments,
entered.employee,
entered.employee,
cr.call_reason
FROM
calls c
INNER JOIN customers cust
ON c.customer_id = cust.customer_id
INNER JOIN employees entered
ON c.enter_emp_id = entered.employee_id
INNER JOIN employees assigned
ON c.enter_emp_id = assigned.employee_id
INNER JOIN call_state cs
ON c.call_state_id = cs.call_state_id
INNER JOIN call_reasons cr
ON c.call_reason_id = cr.call_reason_id
WHERE
c.call_id = $call_id;

SQL/MySQL Query Assitance

I'm writing a query to list the first and last names of all staff who have ever taught any of the same course as Kim (Staff_Fname) Cox (Staff_Lname) or have ever taught in the same room (Loc_id) as Kim Cox.
So far I have written this (and it's full of errors):
SELECT KimClasses.Course_Code, KimClasses.Course_Name, St2.Staff_Fname, St2.Staff_Lname
FROM
(SELECT St.Staff_id, CS.C_SE_id, C.Course_code, C.Course_name
FROM Staff St
JOIN CourseSection CS ON St.Staff_id = CS.Staff_ID
JOIN Location L ON CS.Loc_ID = L.Loc_id
JOIN Course C ON CS.Course_ID = C.Course_ID
WHERE St.Staff_Fname = "Kim"
AND St.Staff_Lname = "Cox") KimClasses
JOIN CourseSection CS2 ON KimClasses.Course_ID = CS2.Course_ID
AND NOT KimClasses.Staff_id = St2.Staff_id;
But I'm not sure on how I would include the part to list the staff that have taught in the same room.
Database schema: http://i.stack.imgur.com/dTGV5.jpg
This should get you started...
Select s.staff_fname,s.staff_lname
from staff s join course_section cs
on s.staff_if = cs.staff_id
where cs.course_id in (select distinct s1.course_id from staff s1 join course_section sc1 where s1.Staff_Fname = 'kim' and s1.Staff_Lname = "Cox" )
OR s.loc_id in (select distinct s2.loc_id from staff s2 join course_section sc2 where s2.Staff_Fname = 'kim' and s2.Staff_Lname = "Cox" )

Speedier alternative to SUBSTRING() in MySQL?

I have a query that uses SUBSTRING() as a criteria:
SELECT p.name p_name,
pa.line1 p_line1,
pa.zip p_zip,
c.name c_name,
ca.line1 c_line1,
ca.zip c_zip
FROM bank b
JOIN import_bundle ib ON ib.bank_id = b.id
JOIN generic_import gi ON gi.import_bundle_id = ib.id
JOIN account_import ai ON ai.generic_import_id = gi.id
JOIN account a ON a.account_import_id = ai.id
JOIN account_address aa ON aa.account_id = a.id
JOIN address ca ON aa.address_id = ca.id
JOIN address pa ON pa.zip = ca.zip OR (pa.zip = ca.zip AND pa.line1 = ca.line1)
JOIN prospect p ON p.address_id = pa.id
JOIN customer c ON a.customer_id = c.id
WHERE b.name = 'M'
AND ib.active = 1
AND gi.active = 1
AND SUBSTRING(p.name, 1, 12) = SUBSTRING(c.name, 1, 12)
LIMIT 100
As you can see, it's just comparing the first 12 characters of p.name and c.name. Unfortunately, adding this query to the WHERE clause makes my query unbearably slow. Are there any tricks out there to do this same comparison, or is my best bet to add another column to each table that contains the first 12 characters of the customer's name? I hope it's not the latter because that would be a lot of work and I'll ultimately be doing several comparisons like this.
Add the extra columns and set up an update trigger to populate them automatically. Be sure to create indexes on the new columns, of course.