MySQL 查詢優化:如果沒有過期則選擇所有重複項,如果一個過期則選擇最新的
我有這個查詢,它有效,我只是想優化,如果可能的話,這是一個完整的工作範例,您可以在其中對其進行測試
- 如果兩個項目具有相同的 bundle_id 或 package_id 或 service_id,則它們被視為重複項
- 如果滿足以下條件,則項目被視為已過期並處於寬限期內
(expires + INTERVAL 5 DAY) >= NOW()
- 一個項目沒有過期,如果
expires >=NOW()
我想做以下
- 如果存在重複,它們都沒有過期,我想全選
- 如果存在重複,其中一些已過期,而另一些則沒有,我想選擇未過期的項目
- 如果存在重複,兩個過期都在寬限期內,我想選擇最近的。
- 我不想選擇過期且不再處於寬限期內的項目
- 如果某個項目沒有重複項並且處於寬限期或尚未過期,請選擇它。
- 該查詢應該在 MySQL 5.5 及更高版本上執行,需要此查詢的客戶端正在升級他的數據庫,尚未完成。
我選擇寬限期內或尚未過期的項目,
order by expire
獲取最新的日期,我LIMIT
用來強制group by
遵守此問題的訂單。然後我group by
用來過濾重複項。然後我補充說,我知道的項目還沒有過期SELECT * FROM ( SELECT wtb_orders.orderid, wtb_orders.duration, wtb_orders.is_renew, wtb_orders.bundle_id, wtb_orders.service_id, wtb_orders.object_name, wtb_orders.pkg_id_fk, expires, wtb_tokens.disconnect_time FROM wtb_orders JOIN wtb_tokens ON wtb_orders.orderid=wtb_tokens.orderid WHERE wtb_orders.username= `96190000002` AND ( ( ((expires + INTERVAL 5 DAY) >= NOW()) OR (expires=0 AND wtb_tokens.orderid IS NOT NULL) ) OR expires=0 ) ORDER by expires DESC LIMIT 1000000 ) as tmp GROUP BY tmp.pkg_id_fk, tmp.bundle_id, tmp.service_id UNION ( SELECT ord.orderid,ord.duration,ord.is_renew,ord.bundle_id,ord.service_id,ord.object_name,ord.pkg_id_fk,tk.expires,tk.disconnect_time FROM wtb_orders AS ord JOIN wtb_tokens AS tk ON ord.orderid=tk.orderid WHERE tk.username= ``96190000002`` AND (((tk.expires >= NOW()) OR (tk.expires=0 AND tk.orderid IS NOT NULL)) OR tk.expires=0) )
我嘗試使用eversql對其進行優化,這在大多數情況下都有幫助,它給了我這個查詢,它沒有執行,我收到一個錯誤,
UNION DISTINCT (SELECT wtb_orders.orderid ...
你可以在這裡檢查錯誤,請滾動到底部Unrecognized statement near type DISTINCT``tmp.pkg_id_fk' in 'group statement
SELECT * FROM (SELECT wtb_orders_orderid, wtb_orders_duration, wtb_orders_is_renew, wtb_orders_bundle_id, wtb_orders_service_id, wtb_orders_object_name, wtb_orders_pkg_id_fk, wtb_tokens_expires, wtb_tokens_disconn_time FROM ((SELECT wtb_orders.orderid AS wtb_orders_orderid, wtb_orders.duration AS wtb_orders_duration, wtb_orders.is_renew AS wtb_orders_is_renew, wtb_orders.bundle_id AS wtb_orders_bundle_id, wtb_orders.service_id AS wtb_orders_service_id, wtb_orders.object_name AS wtb_orders_object_name, wtb_orders.pkg_id_fk AS wtb_orders_pkg_id_fk, wtb_tokens.expires AS wtb_tokens_expires, wtb_tokens.disconnect_time AS wtb_tokens_disconn_time FROM wtb_orders JOIN wtb_tokens ON wtb_orders.orderid = wtb_tokens.orderid WHERE wtb_orders.username = '96190000002' AND ( wtb_tokens.expires = 0 ) ORDER BY wtb_tokens.expires DESC LIMIT 1000000) UNION DISTINCT (SELECT wtb_orders.orderid AS wtb_orders_orderid, wtb_orders.duration AS wtb_orders_duration, wtb_orders.is_renew AS wtb_orders_is_renew, wtb_orders.bundle_id AS wtb_orders_bundle_id, wtb_orders.service_id AS wtb_orders_service_id, wtb_orders.object_name AS wtb_orders_object_name, wtb_orders.pkg_id_fk AS wtb_orders_pkg_id_fk, wtb_tokens.expires AS wtb_tokens_expires, wtb_tokens.disconnect_time AS wtb_tokens_disconn_time FROM wtb_orders JOIN wtb_tokens ON wtb_orders.orderid = wtb_tokens.orderid WHERE wtb_orders.username = '96190000002' AND (((wtb_tokens.expires = 0 AND wtb_tokens.orderid IS NOT NULL))) ORDER BY wtb_tokens.expires DESC LIMIT 1000000) UNION DISTINCT (SELECT wtb_orders.orderid AS wtb_orders_orderid, wtb_orders.duration AS wtb_orders_duration, wtb_orders.is_renew AS wtb_orders_is_renew, wtb_orders.bundle_id AS wtb_orders_bundle_id, wtb_orders.service_id AS wtb_orders_service_id, wtb_orders.object_name AS wtb_orders_object_name, wtb_orders.pkg_id_fk AS wtb_orders_pkg_id_fk, wtb_tokens.expires AS wtb_tokens_expires, wtb_tokens.disconnect_time AS wtb_tokens_disconn_time FROM wtb_orders JOIN wtb_tokens ON wtb_orders.orderid = wtb_tokens.orderid WHERE wtb_orders.username = '96190000002' AND ((((wtb_tokens.expires + INTERVAL 5 DAY) >= NOW()))) ORDER BY wtb_tokens.expires DESC LIMIT 1000000) ) AS union1 ORDER BY union1.wtb_tokens_expires DESC LIMIT 1000000) AS tmp GROUP BY tmp.pkg_id_fk, tmp.bundle_id, tmp.service_id UNION ( SELECT ord.orderid, ord.duration, ord.is_renew, ord.bundle_id, ord.service_id, ord.object_name, ord.pkg_id_fk, tk.expires, tk.disconnect_time FROM wtb_orders AS ord JOIN wtb_tokens AS tk ON ord.orderid = tk.orderid WHERE tk.username = '96190000002' AND ( tk.expires = 0 ) ) UNION DISTINCT (SELECT ord.orderid, ord.duration, ord.is_renew, ord.bundle_id, ord.service_id, ord.object_name, ord.pkg_id_fk, tk.expires, tk.disconnect_time FROM wtb_orders AS ord JOIN wtb_tokens AS tk ON ord.orderid = tk.orderid WHERE tk.username = '96190000002' AND (((tk.expires = 0 AND tk.orderid IS NOT NULL)))) UNION DISTINCT (SELECT ord.orderid, ord.duration, ord.is_renew, ord.bundle_id, ord.service_id, ord.object_name, ord.pkg_id_fk, tk.expires, tk.disconnect_time FROM wtb_orders AS ord JOIN wtb_tokens AS tk ON ord.orderid = tk.orderid WHERE tk.username = '96190000002' AND (((tk.expires >= NOW()))))
即使我設法修復了eversql查詢,我也不相信它比我的更好,好吧它刪除了OR並使用了聯合,但我不得不使用
LIMIT
強制分組來尊重順序,而不是因為它是一件好事。我確信這是一個不好的做法,但我需要一個解決方案。優化的查詢使用 2 個限制,因此它創建了 2 個臨時表。
為了性能,不要試圖在一個語句中完成所有操作。
避免使用非 sargable 表達式,例如
(expires + INTERVAL 5 DAY) >= NOW()
; 而是說(expires >= NOW() - INTERVAL 5 DAY)
可能最好做 3 個查詢來測試 3 個可能的重複(
bundle_id
等)。如果可以在
bundle_id
不查看其他兩個是否有更新的 dup 的情況下選擇最新的過期 dup,那麼就這樣做。那是 3 個單獨的查詢,並在找到一個時停止。如果所有 3 個都找不到 dup,則進入第 4 個查詢 - 查找所有 dup。如果那不行,那麼這 3 個重複中的一個可能
UNION
是最佳的:SELECT FROM ( ( SELECT id FROM ... ) -- find latest bundle_id dup, if any UNION DISTINCT ( SELECT id FROM ... ) -- find latest ... dup, if any UNION DISTINCT ( SELECT id FROM ... ) -- find latest ... dup, if any ) AS x JOIN ... to get other columns ORDER BY ... LIMIT 1 -- Get the latest of the 3 or fewer
由於我在談論多個查詢,因此您需要使用儲存過程以便擁有 IF 語句或使用客戶端程式碼來編排查詢。
IF the above query returned no rows THEN SELECT ... -- all dups END IF
一旦您以這種方式重組了查詢,我將幫助您優化索引。請提供
SHOW CREATE TABLE
以便我們查看數據類型、索引等。(我忽略了有關寬限期的問題;也許這不會使查詢數量增加一倍。)
5.5 可能不會太舊而無法進行優化。