### 13.5.1 准备声明

[](<>)[](<>)

```
PREPARE stmt_name FROM preparable_stmt
```

这[`准备`](prepare.html)statement 准备一条 SQL 语句并为其分配一个名称，*`stmt_name`*，以供稍后参考该声明。准备好的语句执行[`执行`](execute.html)并发布[`解除分配准备`](deallocate-prepare.html).例如，请参阅[第 13.5 节，“准备好的陈述”](sql-prepared-statements.html).

语句名称不区分大小写。*`可准备的_stmt`*是字符串文字或包含 SQL 语句文本的用户变量。文本必须代表单个语句，而不是多个语句。在声明中，`?`字符可用作参数标记，以指示稍后在执行查询时将数据值绑定到查询的位置。这`?`字符不应包含在引号内，即使您打算将它们绑定到字符串值。参数标记只能用于应出现数据值的地方，不能用于 SQL 关键字、标识符等。

如果已存在具有给定名称的预准备语句，则在准备新语句之前将其隐式释放。这意味着如果新语句包含错误并且无法准备，则会返回错误并且不存在具有给定名称的语句。

准备好的语句的范围是创建它的会话，这有几个含义：

-   在一个会话中创建的准备好的语句不适用于其他会话。

-   当会话结束时，无论是正常还是异常，其准备好的语句都不再存在。如果启用了自动重新连接，则不会通知客户端连接丢失。因此，客户端可能希望禁用自动重新连接。看[自动重连控制](https://dev.mysql.com/doc/c-api/8.0/en/c-api-auto-reconnect.html).

-   在存储程序中创建的prepared statement在程序执行完毕后继续存在，以后可以在程序外执行。

-   在存储程序上下文中准备的语句不能引用存储过程或函数参数或局部变量，因为它们在程序结束时超出范围，并且如果稍后在程序之外执行该语句将不可用。作为一种解决方法，请参考用户定义的变量，这些变量也具有会话范围；看[第 9.4 节，“用户定义的变量”](user-variables.html).

    从 MySQL 8.0.22 开始，在准备好的语句中使用的参数在第一次准备语句时确定其类型，并在任何时候保留此类型[`执行`](execute.html)为这个准备好的语句调用（除非该语句被重新准备，如本节后面所述）。此处列出了确定参数类型的规则：

-   作为二元算术运算符的操作数的参数与另一个操作数具有相同的数据类型。

-   如果二元算术运算符的两个操作数都是参数，则参数的类型由运算符的上下文决定。

-   如果参数是一元算术运算符的操作数，则参数的类型由运算符的上下文决定。

-   如果算术运算符没有类型确定上下文，则涉及的任何参数的派生类型都是[`双精度`](floating-point-types.html).例如，当参数是[`选择`](select.html)列表，或者当它是比较运算符的一部分时。

-   作为字符串运算符的操作数的参数具有与其他操作数的聚合类型相同的派生类型。如果运算符的所有操作数都是参数，则派生类型为[`VARCHAR`](char.html);它的排序规则由值决定[`collat​​ion_connection`](server-system-variables.html#sysvar_collation_connection).

-   作为时间运算符的操作数的参数具有类型[`约会时间`](datetime.html)如果运算符返回一个`约会时间`,[`时间`](time.html)如果运算符返回一个`时间`， 和[`日期`](datetime.html)如果运算符返回一个`日期`.

-   作为二元比较运算符的操作数的参数与比较的另一个操作数具有相同的派生类型。

-   作为三元比较运算符的操作数的参数，例如[`之间`](comparison-operators.html#operator_between)具有与其他操作数的聚合类型相同的派生类型。

-   如果比较运算符的所有操作数都是参数，则每个操作数的派生类型为[`VARCHAR`](char.html)，排序规则由值确定[`collat​​ion_connection`](server-system-variables.html#sysvar_collation_connection).

-   作为任何输出操作数的参数[`案子`](flow-control-functions.html#operator_case), [`合并`](comparison-operators.html#function_coalesce), [`如果`](flow-control-functions.html#function_if), [`如果空`](flow-control-functions.html#function_ifnull)， 要么[`NULLIF`](flow-control-functions.html#function_nullif)具有与运算符的其他输出操作数的聚合类型相同的派生类型。

-   如果任何一个的所有输出操作数[`案子`](flow-control-functions.html#operator_case), [`合并`](comparison-operators.html#function_coalesce), [`如果`](flow-control-functions.html#function_if), [`如果空`](flow-control-functions.html#function_ifnull)， 要么[`NULLIF`](flow-control-functions.html#function_nullif)是参数，或者都是`空值`，参数的类型由操作符的上下文决定。

-   如果参数是任何一个的操作数[`案子`](flow-control-functions.html#operator_case), [`合并（）`](comparison-operators.html#function_coalesce), [`如果`](flow-control-functions.html#function_if)， 要么[`如果空`](flow-control-functions.html#function_ifnull), 并且没有类型确定上下文，所涉及的每个参数的派生类型是[`VARCHAR`](char.html)，其排序规则由[`collat​​ion_connection`](server-system-variables.html#sysvar_collation_connection).

-   一个参数，它是 a 的操作数[`投掷（）`](cast-functions.html#function_cast)与指定的类型相同`投掷（）`.

-   如果一个参数是一个直接成员[`选择`](select.html)不属于[`插入`](insert.html)语句，参数的派生类型是[`VARCHAR`](char.html)，其排序规则由[`collat​​ion_connection`](server-system-variables.html#sysvar_collation_connection).

-   如果一个参数是一个直接成员`选择`列表，它是[`插入`](insert.html)语句中，参数的派生类型是插入参数的相应列的类型。

-   如果参数用作`设置`条款[`使现代化`](update.html)声明或`关于重复密钥更新`条款[`插入`](insert.html)语句中，参数的派生类型是由`设置`或`关于重复密钥更新`条款

-   如果参数是函数的参数，则派生类型取决于函数的返回类型。

    对于实际类型和派生类型的某些组合，会触发语句的自动重新编写，以确保与MySQL的早期版本更紧密地兼容。如果以下任何情况属实，则不会进行重新准备：

-   `无效的`用作实际参数值。

-   参数是函数的操作数[`演员阵容`](cast-functions.html#function_cast)（相反，将尝试对派生类型进行转换，如果转换失败，将引发异常。）

-   参数是字符串。（在本例中，隐式`铸造（？AS）*`派生型`*)`是执行的。）

-   参数的派生类型和实际类型都是[`整数`](integer-types.html)并且有相同的标志。

-   参数的派生类型为[`十进制的`](fixed-point-types.html)它的实际类型是`十进制的`或[`整数`](integer-types.html).

-   派生类型为[`双重的`](floating-point-types.html)实际类型是任何数字类型。

-   派生类型和实际类型都是字符串类型。

-   如果派生类型是时态的，而实际类型是时态的。*例外情况*：派生类型为[`时间`](time.html)而实际类型并非如此`时间`; 派生类型为[`日期`](datetime.html)而实际类型并非如此`日期`.

-   派生类型是时态的，实际类型是数字的。

    对于刚列出的情况以外的情况，将重新编写语句，并使用实际参数类型而不是派生参数类型。

    这些规则也适用于准备好的语句中引用的用户变量。

    在准备好的语句中为给定的参数或用户变量使用不同的数据类型，以便在第一次执行之后执行该语句，这会导致重新准备该语句。这样效率较低；它还可能导致参数（或变量）的实际类型发生变化，从而导致结果与准备好的语句的后续执行不一致。由于这些原因，在重新执行准备好的语句时，建议对给定参数使用相同的数据类型。
