简体   繁体   中英

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 !

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.

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

屏幕截图

由于实际的子查询用于获取与每个“进程”相关的“最后”行,因此您既不能将其转换为JOIN,也不能将其用作设置会话变量的单独查询。

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM