derRobert
derRobert

Reputation: 571

How to replace sub-query by joins?

How can i get rif of theses subqueries ?

(all tables have columns Created and LastEdited as timestamps)

table Process
 - ID
 - Title

table ProcessHistory
 - ID
 - ProcessID
 - HistoryID

table History
 - ID
 - Title (new, open, closed etc.) 

When i try to get a list of processes with cols of the last status title i do:

SELECT DISTINCT Process.*, History.Title AS HistoryTitle, History.ID AS HistoryID
FROM `Process`
LEFT JOIN ProcessHistory AS ProcessHistory ON Process.ID=ProcessHistory.ProcessID
LEFT JOIN History AS History ON HistoryID=ProcessHistory.HistoryID
WHERE History.ID = (
    SELECT HistoryID FROM ProcessHistory 
    WHERE ProcessID=Process.ID 
    ORDER BY ProcessHistory.ID DESC LIMIT 1
)
GROUP BY Process.ID
ORDER BY Process.ID DESC LIMIT 0, 100

When i try to get a list filtered by a specific status (Where the latest HistoryID is 1 - "all open Processes")

SELECT DISTINCT Process.*, History.Title AS HistoryTitle, History.ID AS HistoryID
FROM `Process`
LEFT JOIN ProcessHistory AS ProcessHistory ON Process.ID=ProcessHistory.ProcessID
LEFT JOIN History AS History ON HistoryID=ProcessHistory.HistoryID
WHERE History.ID=(
    SELECT HistoryID FROM ProcessHistory 
    WHERE HistoryID =1   
    AND ProcessID=Process.ID ORDER BY ProcessHistory.ID DESC LIMIT 1
)
GROUP BY Process.ID
ORDER BY Process.ID DESC LIMIT 0, 100

For performance reasons i want to get rid of theses subqueries? How can i replace the subquery?

Thanks in advance !

Upvotes: 0

Views: 211

Answers (4)

Will
Will

Reputation: 2191

You'll still need to make another select to filter out the rest of the process history, but you should use a derived table instead of a subquery, like this sqlfiddle:

SELECT Process.*, History.Title AS HistoryTitle
FROM Process
JOIN (
  SELECT ProcessID, max(HistoryID) as HistoryID
  FROM ProcessHistory
  GROUP BY ProcessID
) PH ON PH.ProcessID = Process.ID
JOIN History ON History.ID = PH.HistoryID
ORDER BY Process.ID DESC LIMIT 0, 100

As Himanshu Patel pointed out, your query to "get a list filtered by a specific status (Where the latest HistoryID is 1 - "all open Processes")" will not produce the desired effect. It will simply return all processes that have a HistoryID of 1. See this sqlfiddle.

Instead, you want to use a derived table to get those latest process history ids, join them with the processes, and filter on the history ids like this sqlfiddle:

SELECT DISTINCT Process.*, History.Title AS HistoryTitle
FROM Process
JOIN (
  SELECT ProcessID, max(HistoryID) as HistoryID
  FROM ProcessHistory
  GROUP BY ProcessID
) PH ON PH.ProcessID = Process.ID
JOIN History ON History.ID = PH.HistoryID
WHERE PH.HistoryID = 1
ORDER BY Process.ID DESC LIMIT 0, 100

An alternative approach would be to create a view that filters the ProcessHistory table to the latest history per process and join on that. YMMV, but in some cases, performance can be improved that way.

Upvotes: 0

Uueerdo
Uueerdo

Reputation: 15941

Due to the actual subquery, used to get "last" row related to each "process", you can neither convert it to a JOIN nor can you use it as a separate query to set a session variable.

Upvotes: 0

SIDU
SIDU

Reputation: 2278

SELECT *
FROM Process AS p
LEFT JOIN (
  SELECT ph.ProcessID, ph.HistoryID, h.Title AS HistoryTitle
  FROM ProcessHistory AS ph
  JOIN History AS h ON ph.HistoryID = h.ID
  WHERE h.ID = 1
) AS phh ON p.ID = phh.ProcessID
ORDER BY p.ID DESC LIMIT 100

Screenshot

Upvotes: 0

Himanshu Patel
Himanshu Patel

Reputation: 243

Query you posted not give proper result as per ORDER BY ProcessHistory.ID DESC LIMIT 1

Try below query as per result of your query

 SELECT DISTINCT p.*, h.Title AS HistoryTitle,h.ID AS  HistoryID
FROM
Process p JOIN ProcessHistory ph ON p.ID=ph.ProcessID and ph.HistoryID=1 
JOIN History h ON h.ID=ph.HistoryID
GROUP BY p.ID
ORDER BY p.ID DESC LIMIT 0, 100;

Here the sql fiddle:
sql fiddle link

If you want other result then comment.

Upvotes: 3

Related Questions