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?
Related
I'm trying to join several tables in my database.
I need to get account information from the 'accounts' table with the latest meter history on it.
And if an account has no meter history, I want it to show 'meter' related fields as NULL.
Here's my query so far:
SELECT
accounts.id,
accounts.account_order,
acc.id AS accounts_class_id,
acc.zone,
acc.book,
acc.service_class,
acc.size,
acc.account_no AS series_no,
accounts.status,
application_address.address_line,
concessionaires.firstname,
concessionaires.middlename,
concessionaires.lastname,
mb.brand_name,
m.meter_no,
ms.meter_status
FROM
accounts
INNER JOIN
applications
ON accounts.application_id = applications.id
LEFT JOIN
application_address
ON applications.application_no = application_address.application_no
LEFT JOIN
concessionaires
ON applications.concessionaire_no = concessionaires.concessionaire_no
INNER JOIN
accounts_classifications acc
ON accounts.id = acc.account
INNER JOIN meter_history mh
ON mh.id = (SELECT id FROM meter_history mh2
WHERE mh2.account_id = accounts.id
ORDER BY mh2.status_date DESC
LIMIT 1)
LEFT JOIN
meter_status ms
ON mh.meter_status = ms.id
INNER JOIN
meter m
ON mh.meter = m.id
LEFT JOIN
meter_brand mb
ON m.meter_brand = mb.id
WHERE
acc.book = 1 AND acc.zone = 20 AND applications.status = '6' AND acc.status = '1'
This would return only accounts with meter history on it.
Where should I put my IF condition so I get accounts with no history as well, or if that is even possible with my query. Thank you!
I am trying to get the rounded average rating for each trades person using ROUND(AVG(travisor_review.rating)). However this is returning the average for all reviews and only returning one row, when there should be a row for each trades person and the average for that trades person only. My query is below (I hope you can infer table structure from that).
SELECT travisor_tradesperson.name, travisor_catagory.catname,
travisor_city.cityname, travisor_company.cname,
travisor_company.description, ROUND(AVG(travisor_review.rating))
FROM `travisor_tradesperson`
INNER JOIN travisor_company
ON travisor_tradesperson.company = travisor_company.id
INNER JOIN travisor_catagory
ON travisor_tradesperson.catagory = travisor_catagory.id
INNER JOIN travisor_city
ON travisor_tradesperson.city = travisor_city.id
INNER JOIN travisor_review
ON travisor_review.tradesperson = travisor_tradesperson.id
WHERE travisor_catagory.catname = '$catagory'
AND travisor_city.cityname = '$city'
You need an aggregation
SELECT travisor_tradesperson.name, travisor_catagory.catname,
travisor_city.cityname, travisor_company.cname,
travisor_company.description, ROUND(AVG(travisor_review.rating))
FROM `travisor_tradesperson`
INNER JOIN travisor_company
ON travisor_tradesperson.company = travisor_company.id
INNER JOIN travisor_catagory
ON travisor_tradesperson.catagory = travisor_catagory.id
INNER JOIN travisor_city
ON travisor_tradesperson.city = travisor_city.id
INNER JOIN travisor_review
ON travisor_review.tradesperson = travisor_tradesperson.id
WHERE travisor_catagory.catname = '$catagory'
AND travisor_city.cityname = '$city'
group by travisor_tradesperson.name, travisor_catagory.catname,
travisor_city.cityname, travisor_company.cname,
travisor_company.description
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;
I have a problem in a query.
i need to make a page where user can see all reservations he made with movie name, cinema name, seats code that he reserved
i reached this level
SELECT member.member_username, show_datetime, movie_name
FROM `member`
JOIN `reservation` ON `reservation`.`member_id` = `member`.`member_id`
JOIN `show` ON `show`.`show_id`= `reservation`.`show_id`
JOIN `movie` ON `movie`.`movie_id` = `show`.`movie_id`
WHERE `reservation`.`member_id`=1
i need to make a connection and get also the cinema_name from cinema table and seats_code from seat table and theater name
in fact, i need a query to give me all almost all data in my whole database
here is the schema for the DB
http://imageshack.us/photo/my-images/824/58054944.jpg/
JOIN these two tables too:
SELECT
m.member_username,
sh.show_datetime,
v.movie_name,
c.cinema_name,
t.theater_name
FROM member AS m
INNER JOIN reservation AS r ON r.member_id = m.member_id
INNEr JOIN show AS sh ON sh.show_id = r.show_id
INNER JOIN movie AS v ON v.movie_id = s.movie_id
INNER JOIN theater AS t ON t.theater_id = sh.theater_id
INNER JOIN cinema AS c ON c.theater_id = sh.theater_id
WHERE r.member_id = 1
Keep joining your tables
SELECT member.member_username, show_datetime, movie_name, c.cinema_name, t.theater_name
FROM `member`
JOIN `reservation` ON `reservation`.`member_id` = `member`.`member_id`
JOIN `show` ON `show`.`show_id`= `reservation`.`show_id`
JOIN `movie` ON `movie`.`movie_id` = `show`.`movie_id`
JOIN `theater` ON `show`.`theater_id` = `theater`.`theater_id`
JOIN `cinema` ON `theater`.`cinema_id` = `cinema`.`cinema_id`
JOIN `seat` ON `show`.`theater_id` = `seat`.`theater_id`
WHERE `reservation`.`member_id`=1
These are my tables:
Cadastros (id, nome)
Convenios (id, nome)
Especialidades (id, nome)
Facilidades (id, nome)
And the join tables:
cadastros_convenios
cadastros_especialidades
cadastros_facilidades
The table I'm querying for: Cadastros
I'm using MySQL.
The system will allow the user to select multiple "Convenios", "Especialidades" and "Facilidades". Think of each of these tables as a different type of "tag". The user will be able to select multiple "tags" of each type.
What I want is to select only the results in Cadastros table that are related with ALL the "tags" from the 3 different tables provided. Please note it's not an "OR" relation. It should only return the row from Cadastros if it has a matching link table row for EVERY "tag" provided.
Here is what I have so far:
SELECT Cadastro.*, Convenio.* FROM Cadastros AS Cadastro
INNER JOIN cadastros_convenios AS CadastrosConvenio ON(Cadastro.id = CadastrosConvenio.cadastro_id)
INNER JOIN Convenios AS Convenio ON (CadastrosConvenio.convenio_id = Convenio.id AND Convenio.id IN(2,3))
INNER JOIN cadastros_especialidades AS CadastrosEspecialidade ON (Cadastro.id = CadastrosEspecialidade.cadastro_id)
INNER JOIN Especialidades AS Especialidade ON(CadastrosEspecialidade.especialidade_id = Especialidade.id AND Especialidade.id IN(1))
INNER JOIN cadastros_facilidades AS CadastrosFacilidade ON (Cadastro.id = CadastrosFacilidade.cadastro_id)
INNER JOIN Facilidades AS Facilidade ON(CadastrosFacilidade.facilidade_id = Facilidade.id AND Facilidade.id IN(1,2))
GROUP BY Cadastro.id
HAVING COUNT(*) = 5;
I'm using the HAVING clause to try to filter the results based on the number of times it shows (meaning the number of times it has been successfully "INNER JOINED"). So in every case, the count should be equal to the number of different filters I added. So if I add 3 different "tags", the count should be 3. If I add 5 different tags, the count should be 5 and so on. It works fine for a single relation (a single pair of inner joins). When I add the other 2 relations it starts to lose control.
EDIT
Here is something that I believe is working (thanks #Tomalak for pointing out the solution with sub-queries):
SELECT Cadastro.*, Convenio.*, Especialidade.*, Facilidade.* FROM Cadastros AS Cadastro
INNER JOIN cadastros_convenios AS CadastrosConvenio ON(Cadastro.id = CadastrosConvenio.cadastro_id)
INNER JOIN Convenios AS Convenio ON (CadastrosConvenio.convenio_id = Convenio.id)
INNER JOIN cadastros_especialidades AS CadastrosEspecialidade ON (Cadastro.id = CadastrosEspecialidade.cadastro_id)
INNER JOIN Especialidades AS Especialidade ON(CadastrosEspecialidade.especialidade_id = Especialidade.id)
INNER JOIN cadastros_facilidades AS CadastrosFacilidade ON (Cadastro.id = CadastrosFacilidade.cadastro_id)
INNER JOIN Facilidades AS Facilidade ON(CadastrosFacilidade.facilidade_id = Facilidade.id)
WHERE
(SELECT COUNT(*) FROM cadastros_convenios WHERE cadastro_id = Cadastro.id AND convenio_id IN(1, 2, 3)) = 3
AND
(SELECT COUNT(*) FROM cadastros_especialidades WHERE cadastro_id = Cadastro.id AND especialidade_id IN(3)) = 1
AND
(SELECT COUNT(*) FROM cadastros_facilidades WHERE cadastro_id = Cadastro.id AND facilidade_id IN(2, 3)) = 2
GROUP BY Cadastro.id
But I'm concerned about performance. It looks like these 3 sub-queries in the WHERE clause are gonna be over-executed...
Another solution
It joins subsequent tables only if the previous joins were a success (if no rows match one of the joins, the next joins are gonna be joining an empty result-set) (thanks #DRapp for this one)
SELECT STRAIGHT_JOIN
Cadastro.*
FROM
( SELECT Qualify1.cadastro_id
from
( SELECT cc1.cadastro_id
FROM cadastros_convenios cc1
WHERE cc1.convenio_id IN (1, 2, 3)
GROUP by cc1.cadastro_id
having COUNT(*) = 3 ) Qualify1
JOIN
( SELECT ce1.cadastro_id
FROM cadastros_especialidades ce1
WHERE ce1.especialidade_id IN( 3 )
GROUP by ce1.cadastro_id
having COUNT(*) = 1 ) Qualify2
ON (Qualify1.cadastro_id = Qualify2.cadastro_id)
JOIN
( SELECT cf1.cadastro_id
FROM cadastros_facilidades cf1
WHERE cf1.facilidade_id IN (2, 3)
GROUP BY cf1.cadastro_id
having COUNT(*) = 2 ) Qualify3
ON (Qualify2.cadastro_id = Qualify3.cadastro_id) ) FullSet
JOIN Cadastros AS Cadastro
ON FullSet.cadastro_id = Cadastro.id
INNER JOIN cadastros_convenios AS CC
ON (Cadastro.id = CC.cadastro_id)
INNER JOIN Convenios AS Convenio
ON (CC.convenio_id = Convenio.id)
INNER JOIN cadastros_especialidades AS CE
ON (Cadastro.id = CE.cadastro_id)
INNER JOIN Especialidades AS Especialidade
ON (CE.especialidade_id = Especialidade.id)
INNER JOIN cadastros_facilidades AS CF
ON (Cadastro.id = CF.cadastro_id)
INNER JOIN Facilidades AS Facilidade
ON (CF.facilidade_id = Facilidade.id)
GROUP BY Cadastro.id
Emphasis mine
"It should only return the row from Cadastros if it has a matching row for EVERY "tag" provided."
"where there is a matching row"-problems are easily solved with EXISTS.
EDIT After some clarification, I see that using EXISTS is not enough. Comparing the actual row counts is necessary:
SELECT
*
FROM
Cadastros c
WHERE
(SELECT COUNT(*) FROM cadastros_facilidades WHERE cadastro_id = c.id AND id IN (2,3)) = 2
AND
(SELECT COUNT(*) FROM cadastros_especialidades WHERE cadastro_id = c.id AND id IN (1)) = 1
AND
(SELECT COUNT(*) FROM cadastros_facilidades WHERE cadastro_id = c.id AND id IN (1,2)) = 2
The indexes on the link tables should be (cadastro_id, id) for this query.
Depending on the size of the tables (records), WHERE-based subqueries, running a test on every row CAN SIGNIFICANTLY hit performance. I have restructured it which MIGHT better help, but only you would be able to confirm. The premise here is to have the first table based on getting distinct IDs that meet the criteria, join THAT set to the next qualifier criteria... joined to the FINAL set. Once that has been determined, use THAT to join to your main table and its subsequent links to get the details you are expecting. You also had an overall group by by the ID which will eliminate all other nested entries as found in the support details table.
All that said, lets take a look at this scenario. Start with the table that would be EXPECTED TO HAVE THE LOWEST RESULT SET to join to the next and next. if cadastros_convenios has IDs that match all the criteria include IDs 1-100, great, we know at MOST, we'll have 100 ids.
Now, these 100 entries are immediately JOINED to the 2nd qualifying criteria... of which, say it only matches ever other... for simplicity, we are now matched on 50 of the 100.
Finally, JOIN to the 3rd qualifier based on the 50 that qualified and you get 30 entries. So, within these 3 queries you are now filtered down to 30 entries with all the qualifying criteria handled up front. NOW, join to the Cadastros and then subsequent tables for the details based ONLY on the 30 that qualified.
Since your original query would eventually TRY EVERY "ID" for the criteria, why not pre-qualify it up front with ONE query and get just those that hit, then move on.
SELECT STRAIGHT_JOIN
Cadastro.*,
Convenio.*,
Especialidade.*,
Facilidade.*
FROM
( SELECT Qualify1.cadastro_id
from
( SELECT cc1.cadastro_id
FROM cadastros_convenios cc1
WHERE cc1.convenio_id IN (1, 2, 3)
GROUP by cc1.cadastro_id
having COUNT(*) = 3 ) Qualify1
JOIN
( SELECT ce1.cadastro_id
FROM cadastros_especialidades ce1
WHERE ce1.especialidade_id IN( 3 )
GROUP by ce1.cadastro_id
having COUNT(*) = 1 ) Qualify2
ON Qualify1.cadastro_id = Qualify2.cadastro_id
JOIN
( SELECT cf1.cadastro_id
FROM cadastros_facilidades cf1
WHERE cf1.facilidade_id IN (2, 3)
GROUP BY cf1.cadastro_id
having COUNT(*) = 2 ) Qualify3
ON Qualify2.cadastro_id = Qualify3.cadastro_id ) FullSet
JOIN Cadastros AS Cadastro
ON FullSet.Cadastro_id = Cadastro.Cadastro_id
INNER JOIN cadastros_convenios AS CC
ON Cadastro.id = CC.cadastro_id
INNER JOIN Convenios AS C
ON CC.convenio_id = C.id
INNER JOIN cadastros_especialidades AS CE
ON Cadastro.id = CE.cadastro_id
INNER JOIN Especialidades AS E
ON CE.especialidade_id = E.id
INNER JOIN cadastros_facilidades AS CF
ON Cadastro.id = CF.cadastro_id
INNER JOIN Facilidades AS F
ON CF.facilidade_id = F.id