You should organize the data according to its cardinality.
If you have one header and one footer per client, then these columns can be part of your client table. Since there are clearly multiple body records per client, these should be kept in another table.
If you can have multiple records per client, then your "with hierarchy" model is not bad, although it could be simplified to collapse the Header
and Footer
into Record
since there is only one header and one footer per record.
Your "without hierarchy" model is less desirable. If you have multiple records per client it won't tell you which headers/bodies/footers go together. If you have only one record per client, then having separate tables for header and footer is overkill for the same reason as I noted above for the "with hierarchy" model.
This is what I would recommend, depending on how many records each customer can have:
First consider a query that computes which rows are actually relevant from tablethree
. With the assumption that with "most recently entered result" you mean "most recent enddate
" the following query would gather the appropriate rows:
SELECT sid, MAX(enddate) FROM `tablethree` GROUP BY sid
Now you can build a join to retrieve not only sid
, but all of the data of tablethree
:
SELECT a.*
FROM tablethree a
INNER JOIN (
SELECT sid, MAX(enddate) FROM `tablethree` GROUP BY sid
) b
ON a.sid = b.sid AND a.enddate = b.enddate
This is the result set you actually want to "left join in". You have to insert this into your original query:
SELECT t1.*
FROM tableone AS t1
INNER JOIN tabletwo AS t2
ON t1.cid = t2.id
LEFT OUTER JOIN (
SELECT a.*
FROM tablethree a
INNER JOIN (
SELECT sid, MAX(enddate) FROM `tablethree` GROUP BY sid
) b
ON a.sid = b.sid AND a.enddate = b.enddate
) AS t3
ON t3.sid = t2.sid
WHERE t1.fieldone = 1
AND t1.odate NOT BETWEEN t3.startdate AND t3.enddate
What should also work is the following:
SELECT t1.*
FROM tableone AS t1
INNER JOIN tabletwo AS t2
ON t1.cid = t2.id
LEFT OUTER JOIN tablethree AS t3
ON t3.sid = t2.sid
LEFT OUTER JOIN (
SELECT sid, MAX(enddate) FROM `tablethree` GROUP BY sid
) mostrecent
ON t3.sid = mostrecent.sid AND t3.enddate = mostrecent.enddate
WHERE t1.fieldone = 1
AND t1.odate NOT BETWEEN t3.startdate AND t3.enddate
AND mostrecent.enddate IS NULL
This includes both tablethree
and the new SELECT
as left joins, and sorts out the rows where mostrecent.enddate IS NULL
(meaning those rows which are actually not most recent). This should lead to the same result, but MySQL may be able to compute this result a little faster. EXPLAIN
on both queries should reveal possible differences in computation.
Best Answer
If you're not dead set upon using
OUTER JOIN
a way to get the expected result isotherwise you need to use
FULL OUTER JOIN
a type of join not supported by MySQL, but it can be emulated with a composition like (Full Outer Join in MySQL)