I've got a query as follows:
SELECT COUNT(Table1.Identifier) AS NonCancelCnt
FROM Table1, Table2
LEFT JOIN eventattendees ON eventattendees.AttendeeID = 47322
LEFT JOIN eventsignup ON eventattendees.AttendeeID = eventsignup.AttendeeID
LEFT JOIN transactions on transactions.registrationID=eventsignup.regid
WHERE ((eventsignup.EventID = Table1.Identifier) Or (eventsignup.EventID = Table1.AttendanceLinkID))
The "OR" clause is causing no index to be used. If I remove either portion, my execution path goes from 95,000 to 200, and speed is drastically increased.
I'm not very experienced in reworking such a thing, what is my best option for doing so?
First, you should rewrite your query to specify how Table1, Table2 and eventattendees are joined. Also choose whether you want to specify the columns to use to join in the WHERE clause or after the JOIN keyword. After you clean it up a bit, the optimizer may do a better job of picking the proper index to use.
If that still doesn't work, you can use a SQL hint to specify the index you want the optimizer to use:
WITH INDEX(IX_nameofindex)
SELECT COUNT(Table1.Identifier) AS NonCancelCnt
FROM Table1, Table2
LEFT JOIN eventattendees ON eventattendees.AttendeeID = 47322
LEFT JOIN eventsignup ON eventattendees.AttendeeID = eventsignup.AttendeeID
LEFT JOIN transactions on transactions.registrationID=eventsignup.regid
WHERE eventsignup.EventID = Table1.AttendanceLinkID
union all
SELECT COUNT(Table1.Identifier) AS NonCancelCnt
FROM Table1, Table2
LEFT JOIN eventattendees ON eventattendees.AttendeeID = 47322
LEFT JOIN eventsignup ON eventattendees.AttendeeID = eventsignup.AttendeeID
LEFT JOIN transactions on transactions.registrationID=eventsignup.regid
WHERE eventsignup.EventID = Table1.Identifier
Not understanding what Table1 and Table2 are, nor are they joined in any shape, you will get a Cartesian result (for each record in Table1, will be joined with each record in Table2)
Additionally, your where clause could just be simplified with an IN clause
where
eventsignup.EventID IN ( Table1.Identifier, Table1.AttendanceLinkID )
Related
I have this query.
SELECT WithoutX.Field1, WithoutX.Field3 ,AVG(WithoutX.Field2) AS WO_X, AVG(withX.Field2) AS with_X
FROM (
SELECT ROUND(Table1.Field3,1) as Field3,Table3.Field2,Table2.Field1
FROM Table1
INNER join Table2
ON Table1.Field5 = Table2.Field5 AND instr (Table2.Field1,'X')>0
LEFT JOIN Table3 ON Table3.Field4 = Table1.Field4
) AS withX
INNER JOIN (
SELECT ROUND(Table1.Field3,1) as Field3,Table3.Field2,Table2.Field1
FROM Table1
INNER join Table2
ON Table1.Field5 = Table2.Field5 AND instr (Table2.Field1,'X')=0
LEFT JOIN Table3 ON Table3.Field4 = Table1.Field4
) AS WithoutX ON withX.Field3 = WithoutX.Field3 AND removeX(withX.Field1,'X') = WithoutX.Field1
GROUP BY WithoutX.Field1, WithoutX.Field3
removeX is a function that removes the substring x and other parts of the string in field1, so that the two derived tables WithoutX and WithX can be join based on field1.
It's working but it's very slow. I checked each parts separately and it seams the slowing down comes from the main INNER Join and the Group By. But the function does not change much the speed. Is there any way to optimize this query ?
I am using the SELECT statement below to join the property table with the epc table. Not always is the EPC available for a property. I also want the epc table if the property does not exist.
SELECT p.dateAdded, p.paon, p.saon, p.street, p.locality, p.townCity, p.district, p.county, p.propertyType,
p.propertyType, p.oldNew, p.postcode, p.tenure, p.ppd, p.bedrooms, p.bathrooms, p.receptions, p.lastSalePrice, p.lastTransferDate,
e.INSPECTION_DATE, e.TOTAL_FLOOR_AREA, e.CURRENT_ENERGY_RATING, e.POTENTIAL_ENERGY_RATING, e.CURRENT_ENERGY_EFFICIENCY, e.POTENTIAL_ENERGY_EFFICIENCY,
e.PROPERTY_TYPE
FROM property p
LEFT JOIN epc e ON p.postcode = e.POSTCODE AND CONCAT(p.paon, ', ', p.street) = e.ADDRESS1
WHERE p.paon = 8 AND p.postcode = "TS6 9LN"
ORDER BY e.INSPECTION_DATE, p.lastTransferDate DESC
LIMIT 1
Is it possible to select both tables but if 1 doesn't exist, select the 1 that does?
You need a FULL OUTER JOIN. Unfortunately MySQL does not implement this part of the SQL standard. You can simulate a full outer join with two outer joins, though, but it becomes long, and possibly quite cumbersome and error prone.
For example:
select a.col1, b.col2
from table_a a
LEFT join table_b b on ...
union -- here we union both outer joins
select a.col1, b.col2
from table_a a
RIGHT join table_b b on ...
In the second SELECT the table roles are inverted, since it uses a RIGHT JOIN instead of a LEFT JOIN.
I have a query where I am using a full outer join. But in some instance,
it gives me a syntax error.
What could be the reason for this? I don't see any miscode in my query.
MySQL does not support full outer join, but you can simulate it as a union between a left and right join query:
SELECT * FROM pbsdev3.item t1
LEFT JOIN pbsdev3.item_ledger_entry t2 ON t1.No_ = t2.Item_No_
UNION ALL
SELECT * FROM pbsdev3.item t1
RIGHT JOIN pbsdev3.item_ledger_entry t2 ON t1.No_ = t2.Item_No_
WHERE t1.No_ IS NULL
Note that in general if you find yourself doing full outer joins often, it could imply that your keys and data model are not well defined. One reason why MySQL does not support full joins could be that you should not have to use it.
Full outer join is quite a pain in MySQL. The first thing I would note is that it should not be needed. The items should match in the two tables, so an inner join or left join should be sufficient:
SELECT i.*, ile.*
FROM pbsdev3.item i LEFT JOIN
pbsdev3.item_ledger_entry ile
ON i.No_ = ile.Item_No_;
If you really need full outer join, then gather together all the items and use left join:
select it.*, ile.*
from (select i.No_ from item i union
select ile.Item_No_ from item_ledger_entry ile
) i left join
item it
on it.No_ = i.No_ left join
item_ledger_entry ile
on ile.No = i.No;
I've been puzzling around this problem in mySQL 5.0.51a for quite a while now:
When using LEFT JOIN to join a table AND using a column of the joined table in the WHERE clause, mySQL fails to use the primary index of the joined table in the JOIN, even FORCE INDEX (PRIMARY) fails.
If no column of the joined table is in the WHERE clause, everything works fine.
If the GROUP BY is removed, the index is also used.
Yet I need both of them.
Faulty:
(in my special case up to 1000 secs of exec time)
SELECT *
FROM tbl_contract co
LEFT JOIN tbl_customer cu ON cu.customer_id = co.customer_id
WHERE cu.marketing_allowed = 1 AND co.marketing_allowed = 1
GROUP BY cu.id
ORDER BY cu.name ASC
Working, but not solving my problems:
SELECT *
FROM tbl_contract co
LEFT JOIN tbl_customer cu ON cu.customer_id = co.customer_id
GROUP BY co.id
Table structures (transcribed, as the real tables are more complex)
tbl_contract:
id: INT(11) PRIMARY
customer_id: INT(11)
marketing_allowed: TINYINT(1)
tbl_customer:
customer_id: INT(11) PRIMARY
marketing_allowed: TINYINT(1)
mySQL EXPLAIN notices PRIMARY as possible key when joining, but doesn't use it.
There has been one solution:
SELECT (...)
HAVING cu.marketing_allowed = 1
Solves the problem BUT we use the query in other contexts, where we can only select ONE column in the whole statement, but HAVING needs the marketing_allowed column to be selected in the SELECT-Statement.
I also noticed, that running ANALYZE TABLE on the desired tables will make mySQL 5.5.8 on my local system do the right thing, but I cannot always assure that ANALYZE has been run right before the statement. Anyways, this solution does not work under mySQL 5.0.51a on our productive server. :(
Is there a special rule in mySQL which I didn't notice? Why are LEFT JOIN indexes not used if columns appear in the WHERE clause? Why can't I force them?
Thx in advance,
René
[EDIT]
Thanks to some replies I could optimize the query using an INNER JOIN, but unfortunately, though seeming absolutely fine, mySQL still rejects to use an index when using an ORDER BY clause, as I found out:
SELECT *
FROM tbl_contract co
INNER JOIN tbl_customer cu ON cu.customer_id = co.customer_id AND cu.marketing_allowed = 1
WHERE cu.marketing_allowed = 1
ORDER BY cu.name ASC
If you leave the ORDER BY out, mySQL will use the index correctly.
I have removed the GROUP BY as it has no relevance in the example.
[EDIT2]
FORCING Indexes does not help, as well. So, the question is: Why does mySQL not use an index for joining, as the ORDER BY is executed AFTER joining and reducing the result set by a WHERE clause ? This should usually not influence joining...
I'm not sure I understand what you are asking, but
SELECT *
FROM tbl_contract co
LEFT JOIN tbl_customer cu ON cu.customer_id = co.customer_id
WHERE cu.marketing_allowed = 1 AND co.marketing_allowed = 1
will not do an outer join (because of cu.marketing_allowed = 1).
You probably meant to use:
SELECT *
FROM tbl_contract co
LEFT JOIN tbl_customer cu
ON cu.customer_id = co.customer_id
AND cu.marketing_allowed = 1
WHERE co.marketing_allowed = 1
I had the same trouble. MySQL optimizer is not using indexes while using JOIN with conditions. I changed my SQL statement from JOIN to subqueries :
SELECT
t1.field1,
t1.field2,
...
(SELECT
t2.field3
FROM table2 t2
WHERE t2.fieldX=t1.fieldX
) AS field3,
(SELECT
t2.field4
FROM table2 t2
WHERE t2.fieldX=t1.fieldX
) AS field4,
FROM table1 t1
WHERE t1.fieldZ='valueZ'
ORDER BY t1.sortedField
This request is much more complicated but as indexes are used, it is also much more faster.
You could also use STRAIGHT_JOIN but performance is better with above query. Here's a comparison for by DB with 100k rows in table1 and 20k in table2 :
0.00s using above query
0.10s using STRAIGHT_JOIN
0.30 using JOIN
Have you tried multiple condition on JOIN clause?
SELECT *
FROM tbl_contract co
LEFT JOIN tbl_customer cu ON cu.customer_id = co.customer_id AND cu.marketing_allowed = 1
WHERE co.marketing_allowed = 1
I want to replace the subquery with a join, if possible.
SELECT `fftenant_farmer`.`person_ptr_id`, `fftenant_surveyanswer`.`text_value`
FROM `fftenant_farmer`
INNER JOIN `fftenant_person`
ON (`fftenant_farmer`.`person_ptr_id` = `fftenant_person`.`id`)
LEFT OUTER JOIN `fftenant_surveyanswer`
ON fftenant_surveyanswer.surveyquestion_id = 1
AND fftenant_surveyanswer.`surveyresult_id` IN (SELECT y.`surveyresult_id` FROM `fftenant_farmer_surveyresults` y WHERE y.farmer_id = `fftenant_farmer`.`person_ptr_id`)
I tried:
SELECT `fftenant_farmer`.`person_ptr_id`, `fftenant_surveyanswer`.`text_value`#, T5.`text_value`
FROM `fftenant_farmer`
INNER JOIN `fftenant_person`
ON (`fftenant_farmer`.`person_ptr_id` = `fftenant_person`.`id`)
LEFT OUTER JOIN `fftenant_farmer_surveyresults`
ON (`fftenant_farmer`.`person_ptr_id` = `fftenant_farmer_surveyresults`.`farmer_id`)
LEFT OUTER JOIN `fftenant_surveyanswer`
ON (`fftenant_farmer_surveyresults`.`surveyresult_id` = `fftenant_surveyanswer`.`surveyresult_id`)
AND fftenant_surveyanswer.surveyquestion_id = 1
But that gave me one record per farmer per survey result for that farmer. I only want one record per farmer as returned by the first query.
A join may be faster on most RDBMs, but the real reason I asked this question is I just can't seem to formulate a join to replace the subquery and I want to know if it's even possible.
You could use DISTINCT or GROUP BY, as mvds and Brilliand suggest, but I think it's closer to the query's design intent if you change the last join to an inner-join, but elevating its precedence:
SELECT farmer.person_ptr_id, surveyanswer.text_value
FROM fftenant_farmer AS farmer
INNER
JOIN fftenant_person AS person
ON person.id = farmer.person_ptr_id
LEFT
OUTER
JOIN
( fftenant_farmer_surveyresults AS farmer_surveyresults
INNER
JOIN fftenant_surveyanswer AS surveyanswer
ON surveyanswer.surveyresult_id = farmer_surveyresults.surveyresult_id
AND surveyanswer.surveyquestion_id = 1
)
ON farmer_surveyresults.farmer_id = farmer.person_ptr_id
Broadly speaking, this will end up giving the same results as the DISTINCT or GROUP BY approach, but in a more principled, less ad hoc way, IMHO.
Use SELECT DISTINCT or GROUP BY to remove the duplicate entries.
Changing your attempt as little as possible:
SELECT DISTINCT `fftenant_farmer`.`person_ptr_id`, `fftenant_surveyanswer`.`text_value`#, T5.`text_value`
FROM `fftenant_farmer`
INNER JOIN `fftenant_person`
ON (`fftenant_farmer`.`person_ptr_id` = `fftenant_person`.`id`)
LEFT OUTER JOIN `fftenant_farmer_surveyresults`
ON (`fftenant_farmer`.`person_ptr_id` = `fftenant_farmer_surveyresults`.`farmer_id`)
LEFT OUTER JOIN `fftenant_surveyanswer`
ON (`fftenant_farmer_surveyresults`.`surveyresult_id` = `fftenant_surveyanswer`.`surveyresult_id`)
AND fftenant_surveyanswer.surveyquestion_id = 1
the real reason I asked this question is I just can't seem to formulate a join to replace the subquery and I want to know if it's even possible
Then consider a much simpler example to begin with e.g.
SELECT *
FROM T1
WHERE id IN (SELECT id FROM T2);
This is known as a semi join and if desired may be re-written using (among other possibilities) a JOIN with a SELECT clause to a) project only from the 'outer' table, and b) return only DISTINCT rows:
SELECT DISTINCT T1.*
FROM T1
JOIN T2 USING (id);