I would like to get rid of "Using temporary; Using filesort"
One of the problems I see is that you're using different GROUP BY
and ORDER BY
clauses. From the manual on how MySQL uses temporary tables:
If there is an ORDER BY clause and a different GROUP BY clause, or if the ORDER BY or GROUP BY contains columns from tables other than the first table in the join queue, a temporary table is created.
As soon as you create a temporary table, it will need to be sorted according to your ORDER BY
clause, indicated by 'using filesort'.
This execution plan at leasts uses the indexes to appropriately limit the number of rows found.
I would also look through the docs on ORDER BY optimization.
This was rather longshot but since the OP says it worked, I'm adding it as an answer (feel free to correct it if you find anything wrong).
Try to break the internal query into three parts (INNER JOIN
, LEFT JOIN
with WHERE IS NULL
check, RIGHT JOIN
with IS NULL
check) and then UNION ALL
the three parts. This has the following advantages:
The optimizer has less transformation options available for FULL
joins than for (the more common) INNER
and LEFT
joins.
The Z
derived table can be removed (you can do that anyway) from the view definition.
The NOT(pe.ThisThing = 1 AND se.OtherThing = 0)
will be needed only on the INNER
join part.
Minor improvement, the use COALESCE()
will be minimal if any at all (I assumed that se.SEId
and pe.PEId
are not nullable. If more columns are not nullable, you'll be able to remove more COALESCE()
calls.)
More important, the optimizer may push down any conditions in your queries that involve these columns (now that COALESCE()
is not blocking the push.)
All the above will give the optimizer more options to transform/rewrite any query that uses the view so it may find an execution plan that indexes on the underlying tables can be used.
In all, the view can be written as:
SELECT
se.SEId + '-' + pe.PEId AS Id,
se.SEId, pe.PEId,
pe.StaffName,
pe.EventTime,
COALESCE(pe.EventType, se.EventType) AS EventType,
pe.Duration,
COALESCE(pe.Data, se.Data) AS Data,
COALESCE(pe.Field, se.Field) AS Field,
pe.ThisThing, se.OtherThing,
DATEADD(minute, pe.Duration, pe.EventTime) AS EventEndTime
FROM PE pe INNER JOIN SE se
ON pe.StaffName = se.StaffName
AND pe.Duration = se.Duration
AND pe.EventTime = se.EventTime
WHERE NOT (pe.ThisThing = 1 AND se.OtherThing = 0)
UNION ALL
SELECT
'0-0',
NULL, pe.PEId,
pe.StaffName,
pe.EventTime,
pe.EventType,
pe.Duration,
pe.Data,
pe.Field,
pe.ThisThing, NULL,
DATEADD(minute, pe.Duration, pe.EventTime) AS EventEndTime
FROM PE pe LEFT JOIN SE se
ON pe.StaffName = se.StaffName
AND pe.Duration = se.Duration
AND pe.EventTime = se.EventTime
WHERE NOT (pe.ThisThing = 1)
AND se.StaffName IS NULL
UNION ALL
SELECT
'0-0',
se.SEId, NULL,
se.StaffName,
se.EventTime,
se.EventType,
se.Duration,
se.Data,
se.Field,
NULL, se.OtherThing,
DATEADD(minute, se.Duration, se.EventTime) AS EventEndTime
FROM PE pe RIGHT JOIN SE se
ON pe.StaffName = se.StaffName
AND pe.Duration = se.Duration
AND pe.EventTime = se.EventTime
WHERE NOT (se.OtherThing = 0)
AND pe.StaffName IS NULL ;
Best Answer
To my knowledge, this limitation pertains to SQL specifically in MySQL. All the other SQL products that I am aware of do not have it.
There does not appear to be a solution to this problem in general: you cannot force a column reference to be recognised deeper than one level in MySQL. However, there is an easy workaround in your specific case: replace the
elements.id
in the innermost query withcraft_w8_a.elementId
:This is an equivalent substitute because whatever
craft_w8_a.elementId
will be passed to the IN subquery is going to matchelements.id
based on the filter in the query that passes the value.