I have 3 tables, t1 containing events, t2 containing "attended" events, and t3 containing some invited user ids (linked to system user ids). More than one user can attend and event.
When a new event is created it's inserted into t1. Every time a user confirms attending an event a row is inserted in t2, otherwise the t2 table is empty.
The query bellow pulls all the events an user is attending.
SELECT * FROM events t1
INNER JOIN attending_events t2 ON t1.id_ev = t2.id_ev
INNER JOIN attending_users t3 ON t2.id_user = t3.id_user AND t3.id_system_user=17
WHERE t1.id_comp = 42
Please help, I need a query to do the exact opposite, find the events the user is not attending.
SELECT e.*
FROM events e
WHERE e.id_ev NOT IN
(
SELECT id_ev
FROM attending_events ae
JOIN attending_users au
ON au.id_user = ae.id_user
WHERE au.system_user_id = 17
)
AND id_comp = 42
or:
SELECT *
FROM events e
LEFT JOIN
(
attending_events ae
JOIN attending_users au
ON au.id_user = ae.id_user
)
ON ae.id_ev = e.id_ev
AND au.system_user_id = 17
WHERE e.id_comp = 42
AND ae.id_ev IS NULL
select * from attending_events where id_ev not in
(SELECT id_ev FROM events t1
INNER JOIN attending_events t2 ON t1.id_ev = t2.id_ev
INNER JOIN attending_users t3 ON t2.id_user = t3.id_user AND t3.id_system_user=17
WHERE t1.id_comp = 42)
first thing came to my mind. Definitely should be optimized.
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 have a database with table Plane, the table Flight, which is basically a Flight that a Plane did, then for each flight I have solved M:N table which just holds foreign keys for Flight and Destination, 2 entries in table Destination, to know the difference between Departure and Arrival, table Destination has a connection to table Type( Departure and Arrival, which is set in another table, of 2 rows, "Zacetna" = Departure, "Koncna" = Arrival ). Table Destination also has a connection to table Date which contains Datetime.
I want to Subtract Arrival and Departure time for each Flight, then Sum the times if Plane has multiple Flights.
I have tried to just Subtract them all and then sub but that didn't work, then I have tried to Group By, but with no luck.
In the attached picture.
Plane called "Falcon FX 3000" has 2 flights, idFlight 5 and 6. To get the expected result I would have to do
( Destination_6_Date - Destination_5_Date) + (Destination_4_Date - Destination_7_Date)
but none of my solutions worked
SELECT d.Ime, letD.Destinacija_idDestinacija, l.idLet,tip.Tip, t.Termin
FROM Letalo p
INNER JOIN Let l ON l.Letalo_idLetalo = p.idLetalo
INNER JOIN Let_Has_Destinacija letD on letD.Let_idLet = l.idLet
INNER JOIN Destinacija d on d.idDestinacija = letD.Destinacija_idDestinacija
INNER JOIN Termin t on t.idTermin = d.Termin_idTermin
INNER JOIN Tip_Destinacije tip on tip.idTip_Destinacije = Tip_Destinacije_idTip_Destinacije WHERE p.Naziv='Falcon FX 3000';
https://i.imgur.com/5r2G9yI.png
https://i.imgur.com/3CIfbUN.png
Sorry for the ERDiagram for being in the different language
Letalo = Plane
Let = Flight
Let_Has_Destinacija = Solved M:N, holding Flight ID and Destination ID
Destinacija = Destination
Termin = Date
Tip_Destinacija = Type
EDIT
This is the successful GROUP BY query that returns valid calculations, now I just need to sum them. I am guessing this will be done with subquery, but I don't really understand that, as I am new to MySQL
SELECT d.Ime as Name, letD.Destinacija_idDestinacija as Destination, l.idLet as idFlight,tip.Tip as Type, CAST(t.Termin as DATE), timediff(Max(t.Termin), min(t.Termin))
FROM Letalo p
INNER JOIN Let l ON l.Letalo_idLetalo = p.idLetalo
INNER JOIN Let_Has_Destinacija letD on letD.Let_idLet = l.idLet
INNER JOIN Destinacija d on d.idDestinacija = letD.Destinacija_idDestinacija
INNER JOIN Termin t on t.idTermin = d.Termin_idTermin
INNER JOIN Tip_Destinacije tip on tip.idTip_Destinacije = Tip_Destinacije_idTip_Destinacije WHERE p.Naziv='Falcon FX 3000'
GROUP BY l.idLet;
https://i.imgur.com/hUXZDnQ.png
EDIT 2
By using temp tables, I managed to SUM the flights times, but I have to use 2 queries for this, which is not what I want.
DROP TABLE IF EXISTS tempTime;
CREATE TEMPORARY TABLE tempTime (
SELECT timediff(Max(t.Termin), min(t.Termin)) as FlightTime
FROM Letalo p
INNER JOIN Let l ON l.Letalo_idLetalo = p.idLetalo
INNER JOIN Let_Has_Destinacija letD on letD.Let_idLet = l.idLet
INNER JOIN Destinacija d on d.idDestinacija = letD.Destinacija_idDestinacija
INNER JOIN Termin t on t.idTermin = d.Termin_idTermin
INNER JOIN Tip_Destinacije tip on tip.idTip_Destinacije = Tip_Destinacije_idTip_Destinacije WHERE p.Naziv='Falcon FX 3000'
GROUP BY l.idLet);
SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(FlightTime))) as FlownTime FROM tempTime;
DROP TABLE IF EXISTS tempTime;
https://i.imgur.com/HlPtgHr.png
ACCEPTED ANSWER
SELECT
`Name`,
`Destination`,
`Type`,
`Date`,
SEC_TO_TIME(SUM(TIME_TO_SEC(`Time_diff`))) as `Tot_Diff`
FROM (
SELECT
d.`Ime` as `Name`,
letD.`Destinacija_idDestinacija` as `Destination`,
l.`idLet` as idFlight,tip.`Tip` as `Type`,
CAST(t.`Termin` as DATE) as `Date`,
timediff(Max(t.`Termin`), min(t.`Termin`)) as `Time_diff`
FROM Letalo p
INNER JOIN `Let` l
ON l.`Letalo_idLetalo` = p.`idLetalo`
INNER JOIN `Let_Has_Destinacija` letD
on letD.`Let_idLet` = l.`idLet`
INNER JOIN `Destinacija` d
on d.`idDestinacija` = letD.`Destinacija_idDestinacija`
INNER JOIN `Termin` t
on t.`idTermin` = d.`Termin_idTermin`
INNER JOIN `Tip_Destinacije` tip
on tip.`idTip_Destinacije` = `Tip_Destinacije_idTip_Destinacije`
WHERE p.Naziv = 'Falcon FX 3000'
GROUP BY l.idLet
) as `main`
I believe this should produce the result you describe. Basically, it takes the query you have, uses it as a sub-query
then sums the time differences.
SELECT
`Name`,
`Destination`,
`Type`,
`Date`,
SEC_TO_TIME(SUM(TIME_TO_SEC(`Time_diff`))) as `Tot_Diff`
FROM (
SELECT
d.`Ime` as `Name`,
letD.`Destinacija_idDestinacija` as `Destination`,
l.`idLet` as idFlight,tip.`Tip` as `Type`,
CAST(t.`Termin` as DATE) as `Date`,
timediff(Max(t.`Termin`), min(t.`Termin`)) as `Time_diff`
FROM Letalo p
INNER JOIN `Let` l
ON l.`Letalo_idLetalo` = p.`idLetalo`
INNER JOIN `Let_Has_Destinacija` letD
on letD.`Let_idLet` = l.`idLet`
INNER JOIN `Destinacija` d
on d.`idDestinacija` = letD.`Destinacija_idDestinacija`
INNER JOIN `Termin` t
on t.`idTermin` = d.`Termin_idTermin`
INNER JOIN `Tip_Destinacije` tip
on tip.`idTip_Destinacije` = `Tip_Destinacije_idTip_Destinacije`
WHERE p.Naziv = 'Falcon FX 3000'
GROUP BY l.idLet
) as `main`
GROUP BY `Name`,`Destination`,`Type`,`Date`;
I'm not sure if what I'm looking to do is possible with a union, or if I need to use a nested query and join of some sort.
select c1,c2 from t1
union
select c1,c2 from t2
// with some sort of condition where t1.c1 = t2.c1
Example:
t1
| 100 | regular |
| 200 | regular |
| 300 | regular |
| 400 | regular |
t2
| 100 | summer |
| 200 | summer |
| 500 | summer |
| 600 | summer |
Desired Result
| 100 | regular |
| 100 | summer |
| 200 | regular |
| 200 | summer |
I've tried something like:
select * from (select * from t1) as q1
inner join
(select * from t2) as q2 on q1.c1 = q2.c1
But that joins the records into a single row like this:
| 100 | regular | 100 | summer |
| 200 | regular | 200 | summer |
Try:
select c1, c2
from t1
where c1 in (select c1 from t2)
union all
select c1, c2
from t2
where c1 in (select c1 from t1)
Based on edit, try the below:
MySQL doesn't have the WITH clause which would allow you to refer to your t1 and t2 subs multiple times. You might want to create both t1 and t2 as a view in your database so that you can refer to them as t1 and t2 multiple times throughout a single query.
Even still, the query below honestly looks very bad and could probably be optimized a lot if we knew your database structure. Ie. a list of the tables, all columns on each table and their data type, a few example rows from each, and your expected result.
For instance in your t1 sub you have an outer join with with the LESSON table, but then you have criteria in your WHERE clause (lesson.dayofweek >= 0) which would naturally not allow for nulls, effectively turning your outer join into an inner join. Also you have subqueries that only check for the existence of studentid using criteria that would suggest several tables used don't actually need to be used to produce your desired result. However without knowing your database structure and some example data with an expected result it's hard to advise further.
Even still, I believe the below will probably get you what you want, just not optimally.
select *
from (select distinct students.student_number as "StudentID",
concat(students.first_name, ' ', students.last_name) as "Student",
general_program_types.general_program_name as "Program Category",
program_inventory.program_code as "Program Code",
std_lesson.studio_name as "Studio",
concat(teachers.first_name, ' ', teachers.last_name) as "Teacher",
from lesson_student
left join lesson
on lesson_student.lesson_id = lesson.lesson_id
left join lesson_summer
on lesson_student.lesson_id = lesson_summer.lesson_id
inner join students
on lesson_student.student_number = students.student_number
inner join studio as std_primary
on students.primary_location_id = std_primary.studio_id
inner join studio as std_lesson
on (lesson.studio_id = std_lesson.studio_id or
lesson_summer.studio_id = std_lesson.studio_id)
inner join teachers
on (lesson.teacher_id = teachers.teacher_id or
lesson_summer.teacher_id = teachers.teacher_id)
inner join lesson_program
on lesson_student.lesson_id = lesson_program.lesson_id
inner join program_inventory
on lesson_program.program_code_id =
program_inventory.program_code_id
inner join general_program_types
on program_inventory.general_program_id =
general_program_types.general_program_id
inner join accounts
on students.ACCOUNT_NUMBER = accounts.ACCOUNT_NUMBER
inner join account_contacts
on students.ACCOUNT_NUMBER = account_contacts.ACCOUNT_NUMBER
/** NOTE: the WHERE condition is the only **/
/** difference between subquery1 & subquery2 **/
where lesson.dayofweek >= 0 and
order by students.STUDENT_NUMBER) t1
where StudentID in
(select StudentID
from (select distinct students.student_number as "StudentID",
concat(students.first_name,
' ',
students.last_name) as "Student",
general_program_types.general_program_name as "Program Category",
program_inventory.program_code as "Program Code",
std_lesson.studio_name as "Studio",
concat(teachers.first_name,
' ',
teachers.last_name) as "Teacher",
from lesson_student
left join lesson
on lesson_student.lesson_id = lesson.lesson_id
left join lesson_summer
on lesson_student.lesson_id = lesson_summer.lesson_id
inner join students
on lesson_student.student_number =
students.student_number
inner join studio as std_primary
on students.primary_location_id = std_primary.studio_id
inner join studio as std_lesson
on (lesson.studio_id = std_lesson.studio_id or
lesson_summer.studio_id = std_lesson.studio_id)
inner join teachers
on (lesson.teacher_id = teachers.teacher_id or
lesson_summer.teacher_id = teachers.teacher_id)
inner join lesson_program
on lesson_student.lesson_id = lesson_program.lesson_id
inner join program_inventory
on lesson_program.program_code_id =
program_inventory.program_code_id
inner join general_program_types
on program_inventory.general_program_id =
general_program_types.general_program_id
inner join accounts
on students.ACCOUNT_NUMBER = accounts.ACCOUNT_NUMBER
inner join account_contacts
on students.ACCOUNT_NUMBER =
account_contacts.ACCOUNT_NUMBER
/** NOTE: the WHERE condition is the only **/
/** difference between subquery1 & subquery2 **/
where lesson_summer.dayofweek >= 0
order by students.STUDENT_NUMBER) t2)
UNION ALL
select *
from (select distinct students.student_number as "StudentID",
concat(students.first_name, ' ', students.last_name) as "Student",
general_program_types.general_program_name as "Program Category",
program_inventory.program_code as "Program Code",
std_lesson.studio_name as "Studio",
concat(teachers.first_name, ' ', teachers.last_name) as "Teacher",
from lesson_student
left join lesson
on lesson_student.lesson_id = lesson.lesson_id
left join lesson_summer
on lesson_student.lesson_id = lesson_summer.lesson_id
inner join students
on lesson_student.student_number = students.student_number
inner join studio as std_primary
on students.primary_location_id = std_primary.studio_id
inner join studio as std_lesson
on (lesson.studio_id = std_lesson.studio_id or
lesson_summer.studio_id = std_lesson.studio_id)
inner join teachers
on (lesson.teacher_id = teachers.teacher_id or
lesson_summer.teacher_id = teachers.teacher_id)
inner join lesson_program
on lesson_student.lesson_id = lesson_program.lesson_id
inner join program_inventory
on lesson_program.program_code_id =
program_inventory.program_code_id
inner join general_program_types
on program_inventory.general_program_id =
general_program_types.general_program_id
inner join accounts
on students.ACCOUNT_NUMBER = accounts.ACCOUNT_NUMBER
inner join account_contacts
on students.ACCOUNT_NUMBER = account_contacts.ACCOUNT_NUMBER
/** NOTE: the WHERE condition is the only **/
/** difference between subquery1 & subquery2 **/
where lesson_summer.dayofweek >= 0
order by students.STUDENT_NUMBER) x
where StudentID in
(select StudentID
from (select distinct students.student_number as "StudentID",
concat(students.first_name,
' ',
students.last_name) as "Student",
general_program_types.general_program_name as "Program Category",
program_inventory.program_code as "Program Code",
std_lesson.studio_name as "Studio",
concat(teachers.first_name,
' ',
teachers.last_name) as "Teacher",
from lesson_student
left join lesson
on lesson_student.lesson_id = lesson.lesson_id
left join lesson_summer
on lesson_student.lesson_id = lesson_summer.lesson_id
inner join students
on lesson_student.student_number =
students.student_number
inner join studio as std_primary
on students.primary_location_id = std_primary.studio_id
inner join studio as std_lesson
on (lesson.studio_id = std_lesson.studio_id or
lesson_summer.studio_id = std_lesson.studio_id)
inner join teachers
on (lesson.teacher_id = teachers.teacher_id or
lesson_summer.teacher_id = teachers.teacher_id)
inner join lesson_program
on lesson_student.lesson_id = lesson_program.lesson_id
inner join program_inventory
on lesson_program.program_code_id =
program_inventory.program_code_id
inner join general_program_types
on program_inventory.general_program_id =
general_program_types.general_program_id
inner join accounts
on students.ACCOUNT_NUMBER = accounts.ACCOUNT_NUMBER
inner join account_contacts
on students.ACCOUNT_NUMBER =
account_contacts.ACCOUNT_NUMBER
/** NOTE: the WHERE condition is the only **/
/** difference between subquery1 & subquery2 **/
where lesson.dayofweek >= 0 and
order by students.STUDENT_NUMBER) x);
In order to compare values from two tables you need to do a join. The first way I see is to do 2 queries with inner join and union them:
select t1.* from t1
inner join t2 using (c1)
union
select t2.* from t1
inner join t2 using (c1)
However, we can do better: you can see that I use the exact same query up to which columns I select in the result set, that's a bit of a waste. If we could simply duplicate every records(1)? Well, we can:
select * from t1
inner join t2 using (c1)
cross join (
select 0 as parity
union
select 1 as parity) dup
Now we have everything in double, with an extra column for parity, we just need to select either set of column on that:
-- broken query
select case parity when
0 then t1.*
1 then t2.*
end from t1
inner join t2 using (c1)
cross join (
select 0 as parity
union
select 1 as parity) dup
Unfortunately, the case expression does not allow to return several columns, thus we need to include the test for all of them. In your case, one column is enough:
select c1, case parity when
0 then t1.c2
1 then t2.c2
end from t1
inner join t2 using (c1)
cross join (
select 0 as parity
union
select 1 as parity) dup
order by c1
-- order by parity, c1
Use the commented out order by instead to get the result set for t1 first, an then t2.
Caveat: code untested.
(1) There's this feature called CTE in ANSI SQL (99 I think) which allows to factor out a common query and then reference it throughout the main query, but sadly mysql does not support it.
An UNION is okay.
You want to run the same join twice. Except that the first time over you get the left side, and the second time the right side.
SELECT t1.* FROM t1 JOIN t2 USING (c1)
UNION
SELECT t2.* FROM t1 JOIN t2 USING (c1)
Of course, if at all possible, you could run a single query and save the left side in memory, display the right side, then enqueue the saved left side at end. It needs much more memory than a cursor, but runs the query in half the time (something less actually, due to disk and resource caching).
See here the sample SQLFiddle.
I have two tables that I believe I want to JOIN. I'm very new to this and am not completely sure…
The first table is called venues with the variables id, slug, name, etc. The second table is venue_terms with the variables id, option, venue, value. The matching variables are obviously venues.id and venue_terms.venue.
What I want to do is query venue_terms for matching values and then SELECT * FROM venues that match.
I've been working with the following query, but haven't been able to get it to work. I know INTERSECT isn't the solution, but I'm nut sure which JOIN I should use.
SELECT venue
FROM venue_terms
WHERE `option` = '1' AND `value` = '10'
INTERSECT
SELECT venue
FROM venue_terms
WHERE `option` = '2' AND `value` = '4';
I want to match those venue_terms.venue to the venues table. Can someone point me in the right direction?
UPDATE: To clarify, I'm trying to search multiple option/value combinations that ultimately have the same venue.id's. Basically, I want to able to find all of the venues where (option = 1 and value = 4) AND (option = 2 and value = 10) AND etc… where all of these are true.
You want to find venues that match conditions in two rows in table venue_terms. This can be accomplished by various methods. The most usual is by joining that table twice (another would be by a grouping query).
Here's the first way. Join twice to the venue_terms table:
SELECT v.id --- whatever columns you need
, v.slug --- from the venues table
, v.name
FROM venues AS v
INNER JOIN venue_terms AS vt1
ON vt1.venue = v.id
INNER JOIN venue_terms AS vt2
ON vt2.venue = v.id
WHERE ( vt1.option = 1 AND vt1.value = 10 )
AND ( vt2.option = 2 AND vt2.value = 4 ) ;
If you have 3 conditions, join thrice. If you have 10 conditions, join 10 times. It would be good for the efficiency of the query to have a compound index on (option, value, venue) in the terms table.
try this
SELECT venue.*, venue_terms.*
FROM venue
INNER JOIN venue_terms ON venue.id = venue_terms.venue
WHERE venue_terms.option IN ( 1 ,2)
AND venue_terms.value IN (10,4)
GROUP BY venue.id
How about this?
SELECT t1.*, t2.*
FROM venue t1 JOIN venue_terms t2
ON t1.id = t2.venue
WHERE (t2.option = 1 AND t2.value = 10)
NOTE: I believe option and value are of type INT.
If they are of type varchar then change above query to
SELECT t1.*, t2.*
FROM venue t1 JOIN venue_terms t2
ON t1.id = t2.venue
WHERE (t2.option = '1' AND t2.value = '10')
Update 1
As per your new requirement, you will just need to add that condition with OR option as shown below.
SELECT t1.*, t2.*
FROM venue t1 JOIN venue_terms t2
ON t1.id = t2.venue
WHERE
(t2.option = 1 AND t2.value = 10)
OR
(t2.option = 3 AND t2.value = 14)
This will join the two tables and print out the venues which matches the attributes (option, value) in venue_terms:
SELECT v.* FROM venue v, venue_terms vt
WHERE v.id = vt.venue
AND vt.option = 1
AND vt.value = 10
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