Performance

慢的替代方案LEF噸Ĵ我_ñ+或_大號和F噸Ĵ這一世ñ+這RLEFT JOIN + OR沒有聯合?

  • December 12, 2019

我有一個相當簡單的查詢,它返回給定使用者的所有購買,通過他的政府頒發的 ID 進行搜尋。該文件可能位於兩個不同的表格中。

僅使用左連接的查詢執行速度極慢,如 - 11分鐘

如果不是“OR”,而是執行兩個單獨的查詢,由 a 連接UNION,它們會在 4 秒內產生完全相同的輸出。

儘管可能,但由於外部因素¹,最好避免使用 UNION。

我是否遺漏了導致這種巨大差異的明顯東西?

是否有一種合理的方法來修復 LEFT-JOIN-only 查詢,使其在不到 10 秒內返回?

標準查詢:

SELECT
 purchase.id
FROM
 purchase
 LEFT OUTER JOIN user on purchase.buyer = user.id
 LEFT OUTER JOIN user_documents on user.id = user_documents.user
 LEFT OUTER JOIN buyer_info on purchase.id = buyer_info.purchase
WHERE documents.value = '123' OR buyer_info.document = '123'

使用的 UNION 範例:

SELECT
 purchase.id
FROM
 purchase
 LEFT OUTER JOIN user ON purchase.buyer = user.id
 LEFT OUTER JOIN user_documents ON user.id = user_documents.user
WHERE documents.value = '123'
UNION
SELECT
 purchase.id
FROM
 purchase
 LEFT OUTER JOIN buyer_info ON purchase.id = buyer_info.purchase
WHERE buyer_info.document = '123'

查詢期間所有表中使用的所有欄位都是索引欄位。

PURCHASE 和 BUYER_INFO 表:各約 3200 萬條記錄。

USER 表:約 1600 萬條記錄。

USER_DOCUMENTS 表:約 800 萬條記錄。

這是簡化表的描述:

PURCHASE
| Field | Type | Null | Key | Default |
| id | bigint(20) | NO | PRI | NULL |
| buyer | bigint(20) | YES | MUL | NULL |
USER
| Field | Type | Null | Key | Default |
| id | bigint(20) | NO | PRI | NULL |
USER_DOCUMENTS
| Field | Type | Null | Key | Default |
| id | bigint(20) | NO | PRI | NULL |
| USER | bigint(20) | NO | MUL | NULL |
BUYER_INFO
| Field | Type | Null | Key | Default |
| id | bigint(20) | NO | PRI | NULL |
| purchase | bigint(20) | NO | UNI | NULL |
| document | varchar(14) | YES | MUL | NULL |

我不確定這應該在 StackOverflow 上還是在這裡,但我已經閱讀了幫助中心,在這裡提到query-performance的內容就足夠了。鑑於我在研究這個問題時已經在 StackOverflow 中解決了幾十個問題並且沒有發現任何幫助,我認為這可能是正確的地方。

我已經嘗試過:

  • 將謂詞推入 OUTER JOIN 子句,但無濟於事;
  • 完全刪除兩個 LEFT JOIN 並改用 WHERE EXISTS ——這有幫助,但還不夠;
  • 在適用的情況下使用內部連接——也有幫助,但也沒有關閉。

1 - 外部因素:建構 SQL 的遺留關鍵生產程式碼庫需要進行更大的更改才能將其實現為 UNION。


編輯: 解決一些評論

就正確的數據檢索而言,這兩個查詢都可以完美地工作,包括使用 UNION 的查詢。

此外, UNION 查詢對於我們的目的來說已經足夠快了。

我寧願避免它的唯一原因是因為它需要在生產程式碼庫中進行更多更改才能正常工作。(這裡的查詢經過了極大的清理和過度簡化。)

(這就是標題所說 without UNION的原因)

如果這是唯一明智的做法,那麼我們就去做,但我不明白為什麼添加“OR”會破壞性能,我想也許我忽略了一些東西。

如果我們將第一個查詢分成兩個,它們每個需要 2.5 秒才能執行。但是使用 OR 運算符使其成為單個查詢,突然需要 11 分鐘。

我希望能更好地理解為什麼會這樣,以及是否可以解決它。

如果您從聯合的結果中進行選擇,您將能夠快速過濾您正在尋找的結果,並且只需要一個 where 子句。

SELECT info.id
FROM
 (SELECT
   purchase.id
 FROM
   purchase
 LEFT OUTER JOIN user ON purchase.buyer = user.id
 LEFT OUTER JOIN user_documents ON user.id = user_documents.user
 UNION ALL
 SELECT
   purchase.id
 FROM
   purchase
 LEFT OUTER JOIN buyer_info ON purchase.id = buyer_info.purchase) info
WHERE info.document = '123'

您可以將 WHERE 謂詞推送到連接中,並檢查至少一個連接是否匹配:

SELECT purchase.id
FROM purchase
LEFT JOIN user 
   on purchase.buyer = user.id
LEFT JOIN user_documents 
   on user.id = user_documents.user
  and user_documents.value = '123'  
LEFT JOIN buyer_info 
   on purchase.id = buyer_info.purchase
  and buyer_info.document = '123'
WHERE COALESCE(user_documents.value, buyer_info.document) IS NOT NULL

我假設documents.value 應該是user_documents.value。

引用自:https://dba.stackexchange.com/questions/255340