# 存储函数

在本教程中，您将学习如何使用`CREATE FUNCTION`语句创建存储的函数。

存储的函数是返回单个值的特殊类型的存储程序。您使用存储的函数来封装在SQL语句或存储的程序中可重用的常用公式或业务规则。

与[存储过程](http://www.yiibai.com/mysql/stored-procedure.html)不同，您可以在SQL语句中使用存储的函数，也可以在表达式中使用。 这有助于提高程序代码的可读性和可维护性。

## MySQL存储函数语法

以下说明了创建新存储函数的最简单语法：

```sql
CREATE FUNCTION function_name(param1,param2,…)
    RETURNS datatype
   [NOT] DETERMINISTIC
 statements
```

*首先*，在`CREATE FUNCTION`子句之后指定存储函数的名称。 *其次*，列出括号内存储函数的所有[参数](http://www.yiibai.com/mysql/stored-procedures-parameters.html)。 默认情况下，所有参数均为`IN`参数。不能为参数指定`IN`，`OUT`或`INOUT`修饰符。 *第三*，必须在`RETURNS`语句中指定返回值的数据类型。它可以是任何有效的[MySQL数据类型](http://www.yiibai.com/mysql/data-types.html)。 *第四*，对于相同的输入参数，如果存储的函数返回相同的结果，这样则被认为是确定性的，否则存储的函数不是确定性的。必须决定一个存储函数是否是确定性的。 如果您声明不正确，则存储的函数可能会产生意想不到的结果，或者不使用可用的优化，从而降低性能。 *第五*，将代码写入存储函数的主体中。 它可以是单个语句或复合语句。 在主体部分中，必须至少指定一个`RETURN`语句。`RETURN`语句用于返回一个值给调用者。 每当到达`RETURN`语句时，存储的函数的执行将立即终止。

## MySQL存储函数示例

我们来看一下使用存储函数的例子，这里将使用[示例数据库(yiibaidb)](http://www.yiibai.com/mysql/sample-database.html)中的`customers`表进行演示。

以下示例是根据信用额度返回客户级别的功能。 我们使用[IF语句](http://www.yiibai.com/mysql/if-statement.html)来确定信用额度。

```sql
DELIMITER $$

CREATE FUNCTION CustomerLevel(p_creditLimit double) RETURNS VARCHAR(10)
    DETERMINISTIC
BEGIN
    DECLARE lvl varchar(10);

    IF p_creditLimit > 50000 THEN
 SET lvl = 'PLATINUM';
    ELSEIF (p_creditLimit <= 50000 AND p_creditLimit >= 10000) THEN
        SET lvl = 'GOLD';
    ELSEIF p_creditLimit < 10000 THEN
        SET lvl = 'SILVER';
    END IF;

 RETURN (lvl);
END $$
DELIMITER ;
```

现在，我们在[SELECT语句](http://www.yiibai.com/mysql/select-statement-query-data.html)中调用`CustomerLevel()`存储函数，如下所示：

```sql
SELECT 
    customerName, CustomerLevel(creditLimit)
FROM
    customers
ORDER BY customerName;
```

执行上面查询语句，得到以下结果 -

```sql
+------------------------------------+----------------------------+
| customerName                       | CustomerLevel(creditLimit) |
+------------------------------------+----------------------------+
| Alpha Cognac                       | PLATINUM                   |
| American Souvenirs Inc             | SILVER                     |
| Amica Models & Co.                 | PLATINUM                   |
| ANG Resellers                      | SILVER                     |
| Anna's Decorations, Ltd            | PLATINUM                   |
| Anton Designs, Ltd.                | SILVER                     |
| Asian Shopping Network, Co         | SILVER                     |
| Asian Treasures, Inc.              | SILVER                     |
| Atelier graphique                  | GOLD                       |
| Australian Collectables, Ltd       | PLATINUM                   |
| Australian Collectors, Co.         | PLATINUM                   |
|************** 此处省略了一大波数据 *********************************|
| Vitachrome Inc.                    | PLATINUM                   |
| Volvo Model Replicas, Co           | PLATINUM                   |
| Warburg Exchange                   | SILVER                     |
| West Coast Collectables Co.        | PLATINUM                   |
+------------------------------------+----------------------------+
122 rows in set
```

下面，来重写在[MySQL IF语句教程](http://www.yiibai.com/mysql/if-statement.html)中开发的`GetCustomerLevel()`存储过程，如下所示：

```sql
DELIMITER $$

CREATE PROCEDURE GetCustomerLevel(
    IN  p_customerNumber INT(11),
    OUT p_customerLevel  varchar(10)
)
BEGIN
    DECLARE creditlim DOUBLE;

    SELECT creditlimit INTO creditlim
    FROM customers
    WHERE customerNumber = p_customerNumber;

    SELECT CUSTOMERLEVEL(creditlim) 
    INTO p_customerLevel;
END $$
DELIMITER ;
```

如您所见，`GetCustomerLevel()`存储过程在使用`CustomerLevel()`存储函数时可读性更高。

请注意，存储函数仅返回单个值。 如果没有包含`INTO`子句的`SELECT`语句，则将会收到错误。

另外，如果存储的函数包含SQL语句，则不应在其他SQL语句中使用它; 否则，存储的函数将减慢查询的速度。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hezhiqiang-book.gitbook.io/mysql/di-san-zhang/cun-chu-han-shu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
