MySQL 8.4 參考手冊  /  ...  /  函數名稱解析

11.2.5 函數名稱解析

MySQL 支援內建(原生)函數、可載入函數和儲存函數。本節說明伺服器如何辨識內建函數的名稱是用作函數呼叫還是識別碼,以及當具有給定名稱的不同類型函數存在時,伺服器如何決定要使用的函數。

內建函數名稱解析

解析器使用預設規則來解析內建函數的名稱。這些規則可以透過啟用 IGNORE_SPACE SQL 模式來變更。

當解析器遇到一個是內建函數名稱的單字時,它必須判斷該名稱是指示函數呼叫,還是指對識別碼(例如表格或欄位名稱)的非表達式參考。例如,在下列陳述式中,第一個對 count 的參考是函數呼叫,而第二個參考是表格名稱

SELECT COUNT(*) FROM mytable;
CREATE TABLE count (i INT);

解析器應該僅在解析預期為表達式的內容時,才將內建函數的名稱識別為指示函數呼叫。也就是說,在非表達式內容中,允許將函數名稱作為識別碼。

然而,某些內建函數具有特殊的解析或實作考量,因此解析器預設使用下列規則來區分它們的名稱是用作函數呼叫還是用作非表達式內容中的識別碼

  • 若要在表達式中使用該名稱作為函數呼叫,則名稱與後面的 ( 括號字元之間不得有空格。

  • 相反地,若要將函數名稱用作識別碼,則其後面不得立即接著括號。

名稱與括號之間不得有空格的函數呼叫撰寫要求僅適用於具有特殊考量的內建函數。COUNT 就是這樣一個名稱。sql/lex.h 原始碼檔案列出了這些特殊函數的名稱,這些函數的後續空格會決定它們的解譯:symbols[] 陣列中由 SYM_FN() 巨集定義的名稱。

下列清單列出了 MySQL 8.4 中受 IGNORE_SPACE 設定影響,並且在 sql/lex.h 原始碼檔案中列為特殊的函數。您可能會發現將無空格的要求視為適用於所有函數呼叫最容易。

  • ADDDATE

  • BIT_AND

  • BIT_OR

  • BIT_XOR

  • CAST

  • COUNT

  • CURDATE

  • CURTIME

  • DATE_ADD

  • DATE_SUB

  • EXTRACT

  • GROUP_CONCAT

  • MAX

  • MID

  • MIN

  • NOW

  • POSITION

  • SESSION_USER

  • STD

  • STDDEV

  • STDDEV_POP

  • STDDEV_SAMP

  • SUBDATE

  • SUBSTR

  • SUBSTRING

  • SUM

  • SYSDATE

  • SYSTEM_USER

  • TRIM

  • VARIANCE

  • VAR_POP

  • VAR_SAMP

對於未在 sql/lex.h 中列為特殊的函數,空格並不重要。它們僅在表達式內容中使用時才被解譯為函數呼叫,否則可以自由地用作識別碼。ASCII 就是這樣一個名稱。但是,對於這些不受影響的函數名稱,在表達式內容中的解譯可能會有所不同:func_name () 如果存在具有給定名稱的內建函數,則將其解譯為內建函數;如果沒有,則如果存在具有該名稱的可載入函數或儲存函數,則將 func_name () 解譯為可載入函數或儲存函數。

IGNORE_SPACE SQL 模式可用於修改解析器處理對空格敏感的函數名稱的方式

  • 在停用 IGNORE_SPACE 的情況下,當名稱與後面的括號之間沒有空格時,解析器會將該名稱解譯為函數呼叫。即使在非表達式內容中使用函數名稱,也會發生這種情況

    mysql> CREATE TABLE count(i INT);
    ERROR 1064 (42000): You have an error in your SQL syntax ...
    near 'count(i INT)'

    若要消除錯誤並使該名稱被視為識別碼,請在名稱後面使用空格,或將其寫為加上引號的識別碼(或兩者都使用)

    CREATE TABLE count (i INT);
    CREATE TABLE `count`(i INT);
    CREATE TABLE `count` (i INT);
  • 在啟用 IGNORE_SPACE 的情況下,解析器會放寬函數名稱與後面的括號之間不得有空格的要求。這提供了撰寫函數呼叫時的更大彈性。例如,下列任一函數呼叫都是合法的

    SELECT COUNT(*) FROM mytable;
    SELECT COUNT (*) FROM mytable;

    但是,啟用 IGNORE_SPACE 也會產生副作用,即解析器會將受影響的函數名稱視為保留字(請參閱 第 11.3 節,「關鍵字和保留字」)。這表示名稱後面的空格不再表示其用作識別碼。該名稱可以在有或沒有後續空格的情況下用於函數呼叫,但在非表達式內容中會造成語法錯誤,除非加上引號。例如,在啟用 IGNORE_SPACE 的情況下,下列兩個陳述式都會因語法錯誤而失敗,因為解析器會將 count 解譯為保留字

    CREATE TABLE count(i INT);
    CREATE TABLE count (i INT);

    若要在非表達式內容中使用函數名稱,請將其寫為加上引號的識別碼

    CREATE TABLE `count`(i INT);
    CREATE TABLE `count` (i INT);

若要啟用 IGNORE_SPACE SQL 模式,請使用此陳述式

SET sql_mode = 'IGNORE_SPACE';

IGNORE_SPACE 也會由某些其他複合模式(例如包含在其值中的 ANSI)啟用

SET sql_mode = 'ANSI';

請檢查 第 7.1.11 節,「伺服器 SQL 模式」,以查看哪些複合模式會啟用 IGNORE_SPACE

若要將 SQL 程式碼對 IGNORE_SPACE 設定的依賴性降至最低,請使用下列準則

  • 避免建立與內建函數名稱相同的可載入函數或儲存函數。

  • 避免在非表達式內容中使用函數名稱。例如,這些陳述式使用 count(受 IGNORE_SPACE 影響的受影響函數名稱之一),因此如果啟用 IGNORE_SPACE,無論名稱後面是否有空格,它們都會失敗

    CREATE TABLE count(i INT);
    CREATE TABLE count (i INT);

    如果您必須在非表達式內容中使用函數名稱,請將其寫為加上引號的識別碼

    CREATE TABLE `count`(i INT);
    CREATE TABLE `count` (i INT);

函數名稱解析

下列規則說明伺服器如何解析對函數名稱的參考以進行函數建立和呼叫

  • 內建函數和可載入函數

    如果您嘗試建立與內建函數名稱相同的可載入函數,就會發生錯誤。

    IF NOT EXISTS 在這種情況下無效。如需更多資訊,請參閱 第 15.7.4.1 節,「用於可載入函數的 CREATE FUNCTION 陳述式」

  • 內建函數和儲存函數

    可以建立與內建函數名稱相同的儲存函數,但若要呼叫儲存函數,則必須使用綱要名稱限定它。例如,如果您在 test 綱要中建立名為 PI 的儲存函數,請將其呼叫為 test.PI(),因為伺服器會將沒有限定詞的 PI() 解析為對內建函數的參考。如果儲存函數名稱與內建函數名稱衝突,伺服器會產生警告。可以使用 SHOW WARNINGS 顯示警告。

    IF NOT EXISTS 在這種情況下無效;請參閱 第 15.1.17 節,「CREATE PROCEDURE 和 CREATE FUNCTION 陳述式」

  • 可載入函數與儲存函數

    可以建立與現有可載入函數同名的儲存函數,反之亦然。若提議的儲存函數名稱與現有的可載入函數名稱衝突,或是提議的可載入函數名稱與現有的儲存函數名稱相同,伺服器會產生警告。在兩種情況下,一旦兩個函數都存在,之後在調用儲存函數時必須使用綱要名稱來限定它;在這種情況下,伺服器會假設未限定的名稱是指可載入函數。

    MySQL 8.4 支援 IF NOT EXISTSCREATE FUNCTION 陳述式,但在這種情況下沒有作用。

先前的函數名稱解析規則對升級至實作新內建函數的 MySQL 版本有影響

  • 如果您已建立具有特定名稱的可載入函數,並將 MySQL 升級至實作具有相同名稱的新內建函數的版本,則該可載入函數將變得無法存取。若要更正此問題,請使用 DROP FUNCTION 來刪除可載入函數,並使用 CREATE FUNCTION 來使用不同的不衝突名稱重新建立可載入函數。然後修改任何受影響的程式碼以使用新名稱。

  • 如果新版本的 MySQL 實作了與現有儲存函數同名的內建函數或可載入函數,您有兩種選擇:重新命名儲存函數以使用不衝突的名稱,或變更任何尚未這樣做的函數呼叫,以使用綱要限定詞(schema_name.func_name() 語法)。在任何一種情況下,都要相應地修改任何受影響的程式碼。