iT邦幫忙

0

MySQL 關於 View 呼叫 Function 後查詢資料效率變慢的問題

  • 分享至 

  • xImage

您好,我將問題說明如下:
(1) 資料庫版本 : MySQL 8.0.23
(2) 資料表 名稱 : TB_202104
這個資料表用來儲存 2021-04-01 00:00:00 到 2021-04-30 23:59:59 這一段期間的資料,有 99200872 筆資料
資料表結構如下 :
`---

CREATE TABLE TB_202104 (
OID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
ChannelOID INT(10) UNSIGNED NOT NULL,
Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
Value DOUBLE NOT NULL,
LastUpdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (OID,Timestamp) USING BTREE,
INDEX IDX_ChaOID(ChannelOID) USING BTREE,
INDEX IDX_ChaOID_Timsta(ChannelOID, Timestamp) USING BTREE,
INDEX IDX_Timsta (Timestamp) USING BTREE
)
COMMENT='儲存 2021/04 感測器資料'
COLLATE='utf8_unicode_ci'
PARTITION BY RANGE (unix_timestamp(`Timestamp`))
(PARTITION p0 VALUES LESS THAN (1617840000) ENGINE = InnoDB,
 PARTITION p1 VALUES LESS THAN (1618444800) ENGINE = InnoDB,
 PARTITION p2 VALUES LESS THAN (1619049600) ENGINE = InnoDB,
 PARTITION p3 VALUES LESS THAN (1619827200) ENGINE = InnoDB,
 PARTITION pMax VALUES LESS THAN MAXVALUE ENGINE = InnoDB
);

---`

(3) Function 名稱 : GetMyStartDateTime
Function 結構如下:
`-----------------------------------

CREATE FUNCTION GetMyStartDateTime()
RETURNS timestamp
LANGUAGE SQL
NOT DETERMINISTIC
NO SQL
SQL SECURITY DEFINER
COMMENT '取得 自訂 開始日期時間'
BEGIN 

RETURN '2021-05-01 00:00:00';

END

------------------------------------`

(4) View 表名稱 : DynamicTB
這個 View 表的用途是,呼叫 GetMyStartDateTime() , 過濾 Timestamp 欄位 的日期時間資料
View 表結構如下 :

ALTER ALGORITHM = UNDEFINED SQL SECURITY DEFINER VIEW `DynamicTB` 
    AS 
    select ChannelOID AS ChannelOID,
           Timestamp AS Timestamp,
           Value AS VALUE 
    from TB_202104 
    where (Timestamp >= GetMyStartDateTime()) 
    limit 1 ;

(5) 當我執行以下指令,執行的時間變得異常的漫長,也沒有執行完成

SELECT * FROM DynamicTB

問題是 DynamicTB 這個View 表 裡搜尋的 TB_202104 資料表根本沒有 在 2021-05-01 00:00:00 的資料。
(6) 但是如果我先把 DynamicTB 如下 :

ALTER ALGORITHM = UNDEFINED SQL SECURITY DEFINER VIEW `DynamicTB` 
    AS 
    select ChannelOID AS ChannelOID,
           Timestamp AS Timestamp,
           Value AS VALUE   
    from TB_202104 where (Timestamp >= '2021-05-01 00:00:00') 
    limit 1 ;

然後執行 :

    SELECT * FROM DynamicTB

執行不到 一秒就完成了,因為 TB_202104 根本就沒有 2021-05-01 00:00:00 開始及之後的時間資料。
(7) 目前感覺問題在於 在 DynamicTB 這個 View 表中,只要是呼叫 GetMyStartDateTime() 這個 Function 就會變成異常的慢,甚至根本無法執行完成。
GetMyStartDateTime() 也只是回傳 '2021-05-01 00:00:00' 這個值而已,為何 在 DynamicTB 這個 View 表中呼叫 GetMyStartDateTime() 取得相同的值
就有執行異常的問題?

感謝回覆。

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 個回答

2
一級屠豬士
iT邦大師 1 級 ‧ 2021-09-14 11:22:48
最佳解答

雖然你的函數內是 RETURN '2021-05-01 00:00:00';
對你來說是固定常數,但是對 MySQL 優化器來說, 在
where (Timestamp >= '2021-05-01 00:00:00') 與
where (Timestamp >= GetMyStartDateTime()) 兩者意義不同.
一個是明確的常數不變,另一個是函數,函數就有可能變化,優化器無從得知函數是否回應常數.
所以執行時,必須每一筆都對此函數進行評估,執行一次,自然就變慢了.

建議可以改用另一個table, 只放一筆資料,來達到同樣的效果.
或是改寫成

from TB_202104
   , (select startts from GetMyStartDateTime()) a
where Timestamp >= startts

這樣也許會有幫助,這段我沒有做測試. 只是初步的一個想法.你參考看看.

高級技巧,不懂純推
/images/emoticon/emoticon12.gif/images/emoticon/emoticon12.gif/images/emoticon/emoticon12.gif

感謝 一級屠豬士 解答,有幫我解決這個問題。

0
ckp6250
iT邦好手 1 級 ‧ 2021-09-14 20:59:04

用 view 要小心,效率的確是一大障礙,
早期,我也貪圖 view 的方便,建了數百個 view
目前,幾乎不用它了,改用 stored procedure 啦。

我要發表回答

立即登入回答