Need help to tune a complex query - mysql

I am trying to performance tune a query and need help with that
We have a requirement to pull in data on the website based on multiple factors which resulted in a complex query which works fine but is expensive. We have all the correct indexes.
What I am stuck on is removing the use of DISTINCT in the query (which seems to be the bottleneck).
I am no SQL expert and I think I've tried everything I could.
Any help to simplify this query and remove DISTINCT (or not use GROUP BY) will be much appreciated. Thanks.
SELECT DISTINCT t2.PK
FROM categories t0
JOIN cat2catrel t1 ON t1.SourcePK = t0.PK
JOIN categories t2 ON t1.TargetPK = t2.PK
JOIN cat2catrel t3 ON t3.SourcePK = t2.PK
JOIN categories t4 ON t3.TargetPK = t4.PK
JOIN cat2prodrel t5 ON t5.SourcePK = t4.PK
JOIN products t6 ON t5.TargetPK = t6.PK
JOIN stocklevels t7 ON t7.productcode = t6.code
JOIN relativeinventory t8 ON t7.p_inventory = t8.PK
JOIN warehouses t9 ON t7.warehouse = t9.PK
JOIN pos2warehouserel t10 ON t10.TargetPK = t9.PK
JOIN pos2warehouserel t10 ON t10.TargetPK = t9.PK
JOIN pointofservice item_t11 ON t10.SourcePK = t11.PK
WHERE ( t0.code = 'code'
AND t8.nventorystatus IN (1111)
AND t11.name = 'ABC')
AND ((t0.TypePkString IN (1, 2, 3, 4)
AND (( t0.catalogversion IN (1, 2, 3)))
AND t1.TypePkString=1000
AND t2.TypePkString IN (1, 2, 3, 4)
AND (( t2.catalogversion IN (1, 2)))
AND t3.TypePkString=300
AND t4.TypePkString IN (6, 7, 8)
AND (( t4.catalogversion IN (5, 6)))
AND t5.TypePkString=500
AND t6.TypePkString=600
AND t7.TypePkString=200
AND t8.TypePkString=700
AND t9.TypePkString IN (3, 7)
AND t10.TypePkString=900
AND t11.TypePkString=750 ));
Currently, this query works fine and provides the results I want. I just don't want to use DISTINCT or GROUP BY and still get unique results.
DB: MySQL 6.3

Your only selecting t2.pk, which appears to be the primary key in the categories table. If this is correct, your producing duplication with your Joins. The easiest solution in general to get rid of DISTINCT in this scenario is to start with the table that you are selecting from and ensuring your INNER JOINING to it. However, it appears your actually doing this already, but your duplicating the join... i.e. t0 and t2 are the same table which is a big red flag to me. Try something like this:
SELECT t0.PK
FROM categories t0
INNER JOIN cat2catrel t1 ON t1.SourcePK = t0.PK AND t1.TargetPK = t0.PK
INNER JOIN cat2catrel t3 ON t3.SourcePK = t0.PK
INNER JOIN categories t4 ON t3.TargetPK = t4.PK
INNER JOIN cat2prodrel t5 ON t5.SourcePK = t4.PK
INNER JOIN products t6 ON t5.TargetPK = t6.PK
INNER JOIN stocklevels t7 ON t7.productcode = t6.code
INNER JOIN relativeinventory t8 ON t7.p_inventory = t8.PK
INNER JOIN warehouses t9 ON t7.warehouse = t9.PK
INNER JOIN pos2warehouserel t10 ON t10.TargetPK = t9.PK
INNER JOIN pos2warehouserel t10 ON t10.TargetPK = t9.PK
INNER JOIN pointofservice item_t11 ON t10.SourcePK = t11.PK
WHERE ( t0.code = 'code'
AND t8.nventorystatus IN (1111)
AND t11.name = 'ABC')
AND ((t0.TypePkString IN (1, 2, 3, 4)
AND (( t0.catalogversion IN (1, 2, 3)))
AND t1.TypePkString=1000
AND t2.TypePkString IN (1, 2, 3, 4)
AND (( t2.catalogversion IN (1, 2)))
AND t3.TypePkString=300
AND t4.TypePkString IN (6, 7, 8)
AND (( t4.catalogversion IN (5, 6)))
AND t5.TypePkString=500
AND t6.TypePkString=600
AND t7.TypePkString=200
AND t8.TypePkString=700
AND t9.TypePkString IN (3, 7)
AND t10.TypePkString=900
AND t11.TypePkString=750 ));
However I see other tables duplicated to like "pos2warehouserel". Tables should only be declared and assigned an alias once.

Related

Jquery Datatable: Count records with Left Join and Where clause

I have two tables, 'team' and 'tickets'. Right now its displaying all records and also which ticket the team member is dispatched too.
I now need to count the number of tickets for each team.
http://sqlfiddle.com/#!9/609d4f/18
Here's my attempt:
SELECT
team.techid,
team.name,
tickets.techid,
tickets.customer,
tickets.callstatus,
tickets.serialnumber
FROM team
LEFT JOIN tickets
ON tickets.techid = team.techid AND (tickets.callstatus = 'Dispatch') AND
(COUNT(tickets.customer) WHERE tickets.techid = team.techid )
Update
Working example but only missing the count column:
http://sqlfiddle.com/#!9/609d4f/19
Update2
Tim, thank you for your help but your example doesn't work.
The table should look like this, minus the missing columns of course:
|---------------------|------------------|
| Tech ID | Count |
|---------------------|------------------|
| Tech1 | 1 |
|---------------------|------------------|
| Tech2 | 1 |
|---------------------|------------------|
| Tech3 | 0 |
|---------------------|------------------|
http://sqlfiddle.com/#!9/bfcdf5/1
As you see below, tech1 and tech2 both have records in ato_openservicecalls where the SC_CallStatus is Dispatch
insert into `serviceteam` VALUES (1, 'tech1', 'name1', 'manager1', 'dispatcher1', 'cellphone1');
insert into `serviceteam` VALUES (2, 'tech2', 'name2', 'manager2', 'dispatcher2', 'cellphone2');
insert into `serviceteam` VALUES (3, 'tech3', 'name3', 'manager3', 'dispatcher3', 'cellphone3');
insert into `ato_openservicecalls` VALUES (1, 'tech1', 'Dispatch', 'customer1', 'age1', 'timestamp1', 'serial1', 'comment1');
insert into `ato_openservicecalls` VALUES (2, 'tech2', 'Dispatch', 'customer2', 'age2', 'timestamp2', 'serial2', 'comment2');
insert into `ato_openservicecalls` VALUES (3, 'tech3', 'callstatus3', 'customer3', 'age3', 'timestamp3', 'serial3', 'comment3');
SELECT
t1.techid,
t1.techname,
t1.manager,
t1.dispatcher,
t1.cellphone,
t2.SC_SCTechID,
t2.BCARNA,
t2.SC_CallStatus,
t2.Serial_ID,
t2.Age,
t2.SC_CallTimestamp,
t2.SC_CallComment,
COALESCE(t3.num_tickets, 0) AS num_tickets
FROM serviceteam t1
LEFT JOIN ato_openservicecalls t2
ON t1.techid = t2.SC_SCTechID AND t2.SC_CallStatus = 'Dispatch'
LEFT JOIN
(
SELECT t1.techid, COUNT(*) AS num_tickets
FROM serviceteam t1
INNER JOIN ato_openservicecalls t2
ON t1.techid = t2.SC_SCTechID
WHERE t2.SC_CallStatus = 'Dispatch'
) t3
ON t1.techid = t3.techid;
In MySQL versions earlier than 8+, we can find the counts using a subquery and then join to it:
SELECT
t1.techid,
t1.name,
t2.techid,
t2.customer,
t2.callstatus,
t2.serialnumber,
COALESCE(t3.num_tickets, 0) AS num_tickets
FROM team t1
LEFT JOIN tickets t2
ON t1.techid = t2.techid AND t2.callstatus = 'Dispatch'
LEFT JOIN
(
SELECT t1.techid, COUNT(*) AS num_tickets
FROM team t1
INNER JOIN tickets t2
ON t1.techid = t2.techid
WHERE t2.callstatus = 'Dispatch'
) t3
ON t1.techid = t3.techid;
With MySQL 8+ or later, we can take advantage of analytic functions:
SELECT
t1.techid,
t1.name,
t2.techid,
t2.customer,
t2.callstatus,
t2.serialnumber,
COUNT(t2.customer) OVER (PARTITION BY t1.techid) num_tickets
FROM team t1
LEFT JOIN tickets t2
ON t1.techid = t2.techid AND t2.callstatus = 'Dispatch';
Edit:
You completely changed your question, invalidating my first accepted answer. Here is the new query:
SELECT
t1.techid,
COUNT(t2.customer) AS num_tickets
FROM team t1
LEFT JOIN tickets t2
ON t1.techid = t2.techid AND t2.callstatus = 'Dispatch'
GROUP BY
t1.techid;

Joining two tables, one with identical unique rows and the other one with multiple corresponding rows

I have two tables one of which holds person and parcel information. Every row in this table is unique. There is no repetitve data. It is something as follows;
Additionally, I have this table which includes explanations for these unique rows shown above. For instance
As you can see there are 3 different explanations for just "Jane". I want to add explanation1, explanation2, explanation3 columns to the first table and update those fields with the corresponding information from the second table.
However, the problem emerges exactly at that point. When I select lets say Name, Surname, Parcel and Block fields from both tables to match, all the explanation columns which need to be updated with the relevant explanations are being filled with just with the first explanation. As an example, all the explanation1,explanation2,explanation3 fields for "Jane Black" in the first row of the first table are updated with only "Lorem ipsum dolor sit". The remaining explanations from the second table remains unused.
To sum up, I wonder, is the way I approach the problem not reasonable or is there a way to solve this issue following my approach?
(I utilize both access, mysql as RDBMS etc.)
Quite a nasty way to do it and not tested.
This uses 3 sub queries that get the required explanations for the 1st, 2nd and 3rd explanation columns:-
SELECT t1.name, t1.parcel, t1.block, t1.share, t1.id, t2a.explanation, t2b.explanation, t2c.explanation
FROM table1 t1
LEFT OUTER JOIN
(
SELECT t21.name, t21.parcel, t21.block, t21.share, t21.id, t21.explanation
FROM table2 t21
LEFT OUTER JOIN table2 t22
ON t21.name = t22.name AND t21.surname = t22.surname AND t21.parcel = t22.parcel AND t21.block = t22.block AND t21.share = t22.share AND t21.id = t22.id AND t1.o > t2.no
GROUP BY t21.name, t21.parcel, t21.block, t21.share, t21.id, t21.explanation
HAVING COUNT(t22.No) = 0
) t2a ON t1.name = t2a.name AND t2.surname = t2a.surname AND t1.parcel = t2a.parcel AND t1.block = t2a.block AND t1.share = t2a.share AND t1.id = t2a.id
LEFT OUTER JOIN
(
SELECT t21.name, t21.parcel, t21.block, t21.share, t21.id, t21.explanation
FROM table2 t21
LEFT OUTER JOIN table2 t22
ON t21.name = t22.name AND t21.surname = t22.surname AND t21.parcel = t22.parcel AND t21.block = t22.block AND t21.share = t22.share AND t21.id = t22.id AND t1.o > t2.no
GROUP BY t21.name, t21.parcel, t21.block, t21.share, t21.id, t21.explanation
HAVING COUNT(t22.No) = 0
) t2b ON t1.name = t2b.name AND t2.surname = t2b.surname AND t1.parcel = t2b.parcel AND t1.block = t2b.block AND t1.share = t2b.share AND t1.id = t2b.id
LEFT OUTER JOIN
(
SELECT t21.name, t21.parcel, t21.block, t21.share, t21.id, t21.explanation
FROM table2 t21
LEFT OUTER JOIN table2 t22
ON t21.name = t22.name AND t21.surname = t22.surname AND t21.parcel = t22.parcel AND t21.block = t22.block AND t21.share = t22.share AND t21.id = t22.id AND t1.o > t2.no
GROUP BY t21.name, t21.parcel, t21.block, t21.share, t21.id, t21.explanation
HAVING COUNT(t22.No) = 0
) t2c ON t1.name = t2c.name AND t2.surname = t2c.surname AND t1.parcel = t2c.parcel AND t1.block = t2c.block AND t1.share = t2c.share AND t1.id = t2c.id
Simpler but a bit less reliable is the use GROUP_CONCAT and SUBSTRING_INDEX. This get a string of all the explanations separated with a random string (I have used ~#~##~ here), then splitting that up picking a different one for each column. This will of course fail if an explanation happens to contain the string you use to delimit it.
SELECT t1.name,
t1.parcel,
t1.block,
t1.share,
t1.id,
SUBSTRING_INDEX(GROUP_CONCAT(t2.explanation ORDER BY t2.no SEPARATOR '~#~#~'), '~#~#~', 1),
IF(COUNT(t2.no) >= 2, SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(t2.explanation ORDER BY t2.no SEPARATOR '~#~#~'), '~#~#~', 1), '~#~#~', -1), NULL),
IF(COUNT(t2.no) >= 2, SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(t2.explanation ORDER BY t2.no SEPARATOR '~#~#~'), '~#~#~', 2), '~#~#~', -1), NULL)
FROM table1 t1
LEFT OUTER JOIN table2 t2
ON t1.name = t2.name AND t2.surname = t2.surname AND t1.parcel = t2.parcel AND t1.block = t2.block AND t1.share = t2.share AND t1.id = t2.id
GROUP BY t1.name,
t1.parcel,
t1.block,
t1.share,
t1.id
But personally I would probably do this in code outside SQL.

MySQL: Query and join two tables

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

SQL - Multiple many-to-many relations filtering SELECT

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

MySQL how to join on same table including missing rows

I have a table with text in various language. Its defined like this:
Id|Language|Text
EXAMPLE DATA
0, ENU, a
0, DAN, b
1, ENU, c
2, ENU, d
2, DAN, e
3, ESP, f
3, ENU, g
Language and Id form the key.
Now I want to extract all texts in a langauge (lets say english) and have the coorosponding text in another language (lets say danish) shown in the column next to. So the result should be:
0, a, b
1, c,
2, d, e
3, g
I know I can do a join like this:
SELECT t1.Id, t1.Text AS "ENU", t2.Text AS "DAN" table as t1
JOIN table as t2 ON (t1.Id= t2.Id)
WHERE t1.Langauge = "ENU" AND t2.Language = "DAN";
But this does not include the missing rows (ie row id=1 and id=3). How to do this?
* UPDATE ****
I get suggestion to use LEFT JOIN but I cant get it working. Maybe because my table layout is a bit different than in the simplified question above. My table is defined as this:
Language|MPageId|MFieldId|MParagraph|MText
Where Language,MPageId,MFieldId,MParagraph forms the key
I tried this:
SELECT t1.MPageId, t1.MFieldId, t1.MParagraphId, t1.MText, t2.MText
FROM main as t1 LEFT JOIN main as t2 ON (t1.MPageId = t2.MPageId AND
t1.MFieldId = t2.MFieldId AND t1.MParagraphId = t2.MParagraphId) WHERE
t1.MLanguage = 'ENU' AND t2.MLanguage = 'DAN'
SELECT t1.Id, t1.Text AS "ENU", t2.Text AS "DAN" FROM table as t1
LEFT JOIN table as t2 ON (t1.Id= t2.Id AND t2.Language = "DAN")
WHERE t1.Langauge = "ENU"
You do need the left join... but the "AND" clause for "DAN"ish would be applied AT the LEFT JOIN, and not in the WHERE clause... The where clause implies an INNER JOIN
SELECT
t1.Id,
t1.Text AS "ENU",
t2.Text AS "DAN"
from
YourTable t1
LEFT JOIN YourTable t2
ON t1.Id= t2.Id
AND t2.Language = "DAN"
where
t1.Langauge = "ENU"
You want a Left Join: http://www.tizag.com/mysqlTutorial/mysqlleftjoin.php
select Id,
MAX(case when LanguageS='ENU' then Text else null end ) as A,
MAX( case when LanguageS<>'ENU' then Text else null end ) as B
from LAN
GROUP BY 1
Id A B
0 a b
1 c ?
2 d e
3 g f