文章目录
13.1 数据定义语句
13.1.1 修改DATABASE 语法
原文 P1862
ALTER DATABASE
允许你修改一个数据库的字符集( 需要ALTER
权限 ),要查某看个数据库的字符集文件db.opt
,可以在mysql服务器的data目录下相应的数据库文件夹内看到。
ALTER SCHEMA
是ALTER DATABASE
的同义词。
CHARACTER SET
部分决定了数据库所使用的字符集,COLLATE
决定数据库的校对字符集,关于这两个字符集请见第10章。可以使用SQL命令SHOW CHARACTER SET
和SHOW COLLATION
来查看mysql服务器所支持的这两个字符集分别都有什么。
如果你修改了这两个字符集,使用了这个数据库的的存储例程就要删除并重新建立,以便它使用新的字符集( 在存储例程中,如果未明确指定字符集或排序规则,则具有字符数据类型的变量将使用数据库缺省值, 详情参见章节13.1.16 )
从5.1 之前的版本进行升级
语法中还有另一种使用情况UPGRADE DATA DIRECTORY NAME
, 它是用来新的编码方法来编码某个数据库对应存储文件目录的名字的,这种方法出现在Mysql5.1之后,实现了数据库名字到存储文件目录的映射(映射可以看章节9.2.3)。它被用在以下几种情况中:
- 将旧版本的MySQL升级到5.1或更高版本时
- 当前数据库的存储文件目录名字含有特殊字符(如-),你想用当前的编码方式重新进行编码
- 在
mysql_check
(或者mysql_upgrade
)中使用
例子:
假如在MySQL 5.0中有个数据库为a-b-c
,其对应的目录也会是a-b-c
,然而包含-(或者更特殊的字符),这种命名方式对所有系统来说并不一定安全。在 MySQL 5.1 之后这种目录会被重新命名为a@002db@002dc
。
如果你从5.1之前进行更新之后,你会发现之前那个a-b-c
数据库变成了#mysql50#a-b-c
这个名字,你使用这个数据库名字还得加上前缀#mysql50#
,为了继续使用a-b-c
这种简便形式请使用如下方法
注意 UPGRADE DATA DIRECTORY NAME
在MySQL 5.7.6中已弃用,将在未来的MySQL版本中删除。
自定义例子
此例子针对UPGRADE DATA DIRECTORY NAME
这种情况.
当前使用数据库MySQL 5.7.20,在我的数据库中有个名为test的数据库(存储引擎是MyISAM的,这种可以直接拷走使用;InnoDB就不行了),我把test拷贝一份到同目录下命名为a-b-c
,这时刷新数据库可以看到#mysql50#a-b-c
这个数据库
执行下面的代码并刷新数据库
ALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME;
结果是这样的
13.1.2 修改EVENT语法
原文 P1683
在不删除某一事件的基础上,ALTER EVENT
用来修改此事件的一些属性。它里面的一些特性的用法和含义,如ON SCHEDULE
已在章节13.1.12 中讲过。
任何具有EVENT
权限的用户都可以执行ALTER EVENT
操作,执行ALTER EVENT
成功的用户就会成为此事件的DEFINER
。修改一个不存在的事件会报1517错误。
1.部分用法
(1)可以使用RENAME TO
重命名原事件的名字
(2)关闭一个事件可以使用ALTER EVENT myevent DISABLE;
(3)可以把一个事件移动到新的数据库,使用RENAME TO db_name.event_name
2.注意事项
(1)你在ALTER EVENT
中修改的选项会覆盖原事件的相应属性,未修改的属性会保留。:
(2)在创建或修改事件内嵌套 创建或修改事件不会报错,但是执行的时候会有错误,别这么使用。
13.1.3 修改函数语法
原文 P1865
ALTER FUNCTION
用来修改存储函数的某些属性,你可以修改多个属性,但有些属性不能修改那就是存储函数的主体和参数。如果想要主体和参数的更改,必须使用DROP FUNCTION name
删除函数和CREATE FUNCTION name
重新创建函数。
使用这个方法,你还要有ALTER ROUTINE
权限(该权限自动授予函数的创建者),如果启用了二进制日志记录,则ALTER FUNCTION
语句可能还需要该 SUPER
特权。
更多此方法的关键字请见章节13.1.13 创建函数语法
13.1.6 修改PROCEDURE语法
原文 P1865
ALTER PROCEDURE
用来修改存储过程的某些属性,你可以修改多个属性,但有些属性不能修改那就是存储过程的主体和参数。如果想要主体和参数的更改,必须使用DROP PROCEDURE name
删除函数和CREATE PROCEDURE name
重新创建函数。
使用这个方法,你还要有ALTER ROUTINE
权限(该权限自动授予函数的创建者)。
更多此方法的关键字请见章节13.1.16 创建函数语法
13.1.11 创建DATABASE语法
CREATE DATABASE
用来创建一个指定名字的数据库,你需要CREATE
权限
Mysql中的数据库和表的关系用文件目录和文件来对应实现,所以你在mysql的data目录下建立的文件夹都会被当做一个数据库(没事不要随便这么搞).
你也可以用mysql提供的工具mysqladmin
来建立数据库,详见章节4.5.2
13.1.12 创建EVENT语法
原文 P1894
使用此语句将创建并调度一个新的事件,如果你的事件调度器(Event Scheduler)没有设置为ON状态,这个新的事件不会执行,关于事件调度器请见章节 23.4.2
CREATE EVENT
需要EVENT权限,如果DEFINER
定义了一个高权限的的用户,你的权限也要很高,否则你创建不了。
1.主要构成
创建一个新事件的语法最少需要三个部分:
第一部分CREATE EVENT
限定了新事件的名字(需要唯一,不超过64个字符,大小写不敏感)
第二部分ON SCHEDULE
决定了事件执行的时间间隔
第三部分DO
决定了事件要执行的SQL语句
这里有个例子, 定义了一个名为myevent的事件,一小时之后把mytable表中的所有mycol值都+1:
CREATE EVENT myevent
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR
DO UPDATE myschema.mytable SET mycol = mycol + 1;
DEFINER
部分指定了事件执行时候检查其执行权限的用户是谁,默认是当前执行创建事件的用户,如果你指定了值,此值必须是msyql用户之一,其形式可以是'user_name'@'host_name', CURRENT_USER, CURRENT_USER()
这三种之一,更多事件安全的问题请见 章节23.6
IF NOT EXISTS
和你创建表时候的意思一样,如果当前事件已经存在,则不会创建新的事件,只是会有个警告信息而已。
ON SCHEDULE
有两种形式,你可以任选其一:
-
AT timestamp
表明是一次性事件,timestamp必须是一个日期时间值,不能只有日期而没有时间,你可以指定其为 DATETIME类型或者TIMESTAMP类型。如果你指定的日期时间是过去的,会生成一条警告信息,例如:
+ INTERVAL interval
部分用来指定时间间隔,interval
包含两个部分,一个整数和一个时间单位,联合的时间表达式也可以,比如2分10秒可以写成+ INTERVAL '2:10' MINUTE_SECOND
(等价于+ INTERVAL 2 MINUTE + INTERVAL 10 SECOND
)更多联合时间请见定义部分 -
Every
表明是重复性事件,可以使用interval
部分,不能用+ INTERVAL
。EVERY '2:10' MINUTE_SECOND
表明每2分10秒执行一次该事件。
Every选项还有两个附加参数STARTS
和ENDS
其意思很明确,指定事件开始和结束的时间,如果不指定STARTS
参数,它等价于取事件创建完毕的时间。一个使用例子:
EVERY 12 HOUR STARTS CURRENT_TIMESTAMP INTERVAL 30 MINUTE ENDS CURRENT_TIMESTAMP + INTERVAL 4 WEEK
它表明从30分钟以后开始到4周以后结束,期间每12小时执行一次事件。
如果一个重复性事件没有在其周期内完成,可能会导致同一时间内有多个事件实例进程在运行,如果你想避免这种行为可以使用保护机制,例如GET_LOCK()
方法或者 给行、表上锁
ON SCHEDULE
里面还可以用mysql函数或用户变量来存储时间间隔(下图所示),但是不能用存储函数或用户自定义的函数来存时间间隔,table references也不行.
还有一种情况也不允许存储时间间隔,我不知道怎么用,把原文贴上来
在ON SCHEDULE
部分的时间表现值由会话中的变量time_zone
来决定,默认使用UTC。
2.可用部分
(1)如果你想在事件结束后保留这个实例,可以增加ON COMPLETION PRESERVE
字段
(2)事件创建完毕是否马上启用取决于ENABLE
和DISABLE
选项,这在你修改事件语句的时候很有用。第三个参数DISABLE ON SLAVE
不太明白(原文:is set for the status of an event on a replication slave to indicate that the event was created on the master and replicated to the slave, but is not executed on the slave
)
(3)使用COMMENT 'string'
,你还可以为事件提供一个注释说明,长度在64位以内
3.注意事项
(1)mysql的 sql_mode
变量会影响事件的创建、修改和执行
(2)创建事件中的SQL语句可以是修改事件的语句,但是事件执行的时候会失败。
(3)创建事件中的SQL语句可以是select或show
语句,但是其查询结果不会显示,你可以用select ... into
等存储结果的语句来看查询结果。
(4)创建事件中的SQL语句如果很长,你可以用BEGIN ...END
来包裹。例子如下(使用了delimiter来重新定义结束符):
(5)无法给事件传递参数或者从事件传递参数,但是可以在事件中调用带参数的存储过程。
(6)如果事件的定义者(difiner)有足够的权限来设定全局系统变量,他定义的事件就可以滥用或设定全局变量,请小心使用。
4.自定义例子
事件:
CREATE DEFINER='bbs'@'localhost' EVENT bbs_blog.addopt on SCHEDULE EVERY 10 SECOND STARTS CURRENT_TIMESTAMP + INTERVAL '1:10' MINUTE_SECOND ENDS CURRENT_TIMESTAMP + INTERVAL 2 MINUTE + INTERVAL 10 SECOND on COMPLETION PRESERVE COMMENT 'testevent' DO UPDATE bbs_blog.test set sid=sid+1 WHERE id=2;
这里定义了一个事件 bbs_blog.addopt其作用是在1分10秒后到2分10
之间每10秒将bbs_blog数据库的表test中id为2的记录sid字段+1,将此事件存储在mysql数据库的event表中并提供了注释说明。
问题:
1分10秒后sid没有增加,直到事件区间结束也没有增加。
解决:
(1)设置事件调度器为打开状态
show VARIABLES like '%event_scheduler%';
结果是OFF
打开:set global event_scheduler=ON
;
(2)在event表中删除对应的事件,并再次创建事件
DELETE from mysql.event where name='addopt1';
13.1.13 创建函数语法
CREATE FUNCTION
被用来创建存储函数和用户自定义函数(UDF)
- 有关创建存储函数的内容,请见章节13.1.16 创建存储过程和创建函数的语法
- 有关创建用户自定义函数的内容,请见章节13.7.3.1 创建用户自定义函数的语法
13.1.16 创建过程和创建函数的语法
原文 P1906
这些语句被用来创建存储例程(stored routines),默认情况下,例程与当前数据库关联,为了与指定的数据库进行关联,你可以使用db_name.sp_name
来给它起个名字。
CREATE FUNCTION
还被用来支持用户自定义函数(UDF),UDF算是一种扩展的存储函数,存储函数与UDF共享其命名空间。
要调用存储过程,请使用CALL
语句(章节13.2.1 CALL语法);要调用存储函数,请在表达式中引用它。函数在表达式求值完毕返回一个值。
1.可用部分
(1)DEFINER 和 SQL SECURITY
部分指定了一个安全的上下文(此上下文用来检查例程执行时的权限)
(2)IN OUT INOUT
这三个选项用在创建一个过程中,代表参数的性质。IN
表示传递给过程的参数,OUT
代表从过程中返回的参数(被初始化为NULL),INOUT
表示参数可以由调用者和过程共同修改,调用者做初始值,过程可以修改参数值并返回给调用者。
对于每个OUT
和INOUT
参数,你可以通过CALL
在调用过程中传递参数并获取结果;如果从其他的存储过程或函数中调用一个过程,也可以将常规的参数或局部常规变量作为OUT 或INOUT参数进行传递;如果你在触发器中调用一个过程,可以使用NEW.col_name
作为一个IN
或OUT
参数。
(3)RETURNS
部分是创建函数才能用的,而且是必须有的一部分。它指定了函数返回的类型,函数体必须 包含RETURN xxx
语句 如果RETURN部分返回的类型和RETURNS不一样,它会被强制转化为适合的类型。比如,RETURNS中返回一个枚举或集合的值,RETURN返回了一个整数,强制转换后是一个枚举或集合成员的值对应的字符串。下面是一个创建函数的例子:
(3)routine_body
部分包含了一个SQL例程语句,可以是简单的SELECT语句,也可以是大量复合的语句,多条语句可以用 BEGIN ...END
包裹 ,复合语句请见章节 13.6
(4)COMMENT
部分是MySQL扩展,是一个用于描述存储例程的说明,可以通过 SHOW CREATE PROCEDURE name
和 SHOW CREATE FUNCTION name
来查看。
(5)LANGUAGE
特性表示例程是用什么程序语言编写的。服务器忽略了这个特性,仅支持SQL语言的例程。
(6)一个例程如果输入相同参数总能获得相同结果,那他就是确定性的 DETERMINISTIC
,否则就是不确定性的 NOT DETERMINISTIC
,默认的参数就是 NOT DETERMINISTIC,你可以显示地指定 DETERMINISTIC。
(7){ CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
这几个特征提供了关于例程使用数据的性质的信息,在msyql中这些特征只供参考不会被用来约束和影响一个例程的各种操作。
CONTAINS SQL
表明例程语句不包含数据的读写操作,这个是默认值NO SQL
表示该例程不包含SQL语句。READS SQL DATA
表示例程包含读取数据的语句(例如 SELECT),但不包含写入数据的语句。MODIFIES SQL DATA
表示该例程包含可能写入数据的语句(例如, INSERT或 DELETE)。
(8)SQL SECURITY
选项可以是 DEFINER
或INVOKER
以指定的安全上下文; 也就是说,例程是使用DEFINER子句中指定的帐户的权限执行,还是使用调用它的用户权限执行。此帐户必须具有访问与例程关联的数据库的权限。默认值为 DEFINER(也就是SQL SECURITY DEFINER)。调用例程的用户必须具有该EXECUTE特权,如果例程想在DEFINER环境下运行,当前用户需要是DEFINER指定的用户。
(9) DEFINER
部分用来指定例程执行的用户并检查权限,此例程可能需要在后面有SQL SECURITY DEFINER
这一部分,如果指定DEFINER子句
,则这些规则可以用来确定有效的DEFINER
用户值:
- 如果您没有
SUPER
权限,则唯一允许的user 值是您自己的帐户,无论是字面指定还是使用CURRENT_USER
。您无法将定义者设置为其他帐户。 - 如果您具有
SUPER
权限,则可以指定任何语法上有效的帐户名称。如果该帐户不存在,则会生成警告。 - 尽管可以使用不存在的帐户创建例程, 但如果
SQL SECURITY
值为 DEFINER但定义者帐户不存在,则会在例程执行时发生错误。
下面是一个例子,让我们来理解一下DIFINER
和SQL SECURITY XXX
的性质
a.无论哪个用户定义了该过程,都会为该过程分配DEFINER
为用户 admin@localhost
b.无论哪个用户调用该过程,它都以该帐户admin@localhost
的权限执行(因为默认的安全特性是DEFINER,这里的意思是在BEGIN前面默认有个选项SQL SECURITY DIFINER
也就是第(8)条里面讲的)
c.该过程成功或失败取决于调用者是否具有EXECUTE
权限并且admin
用户具有mysql.user表的SELECT
权限。
让我们再看一个例子:
相较前一个例子,这里多了一个SQL SECURITY INVOKER
语句,
a.无论哪个用户定义了该过程,都会为该过程分配DEFINER
为用户 admin@localhost
b.无论哪个用户调用它,它都以调用者的权限执行例程
c.该过程成功或失败取决于调用者是否具有EXECUTE
权限并且具有mysql.user表的SELECT
权限。
(10)服务器处理例程参数的数据类型并检查,或使用DECLARE
定义的本地例程变量 ,或者函数的返回值如下所示:
- 检查分配数据类型不匹配和溢出。转换和溢出问题会导致警告或严格SQL模式中的错误
- 只能分配标量值?。例如
SET x = (SELECT 1, 2)
是无效的 - 对于字符数据类型,如果
CHARACTER SET
声明中有属性,则使用指定的字符集及其默认排序规则。如果该COLLATE
属性也存在,则使用该排序规则而不是默认排序规则。
如果CHARACTER SET
和COLLATE
这两个字符集都没有指定,例程创建的时候指定的数据库的对应字符集会被使用,如果你想修改例程中的这两个字符集,你可以使用。
2.注意事项
(1)CREATE PROCEDURE
和 CREATE FUNCTION
这两个方法需要CREATE ROUTINE
权限,可能还需要SUPER
权限,具体取决于DEFINER值。如果启用了二进制日志记录,CREATE FUNCTION
还需要SUPER
权限。
(2)默认情况下,MySQL会自动把权限 ALTER ROUTINE
和EXECUTE
赋予创建例程的用户,可以通过禁用automatic_sp_privileges
系统变量来更改此行为
(3)如果例程名称与内置SQL函数的名称相同,则会发生语法错误,除非你在定义或执行例程的时候在例程名字和括号之间多加个空格。所以避免使用内置SQL函数名或已有的你自己的存储例程名字。
(4)sql_mode
变量有一个选项IGNORE_SPACE
(忽略函数名和括号之间的空格),这个选项只对MySQL内置函数有效,对存储例程无效(无论选项是否开启,都允许例程名和括号之间有空格)。
(5)括号内的是参数列表,参数大小写不敏感,空参数列表可以这么表示()
。当你创建一个过程时,列表中参数默认都是IN
类型,可以在参数前指定OUT
和INOUT
这两种选项;当你创建一个函数时,列表中参数只能是IN
类型,无法指定另两种选项。
(6)在例程中准备的语句中不能引用例程参数,详情见附录C.1存储程序的限制
上面的例子是个简单的存储过程,使用了OUT
类型的参数res,你可以用select * from mysql.proc where name='myp_add5'
来查看这个存储过程。
(7)mysql的 sql_mode变量会影响例程的创建、修改和执行
(8)返回结果集的语句可以在存储过程中使用,但不能在存储的函数中使用。这些情况是禁止的 select语句没有添加 into var_list选项、 SHOW
, EXPLAIN
、CHECK TABLE
等。
如果函数定义时就能检测到这种禁止情况,会返回一个错误(ER_SP_NO_RETSET
)
如果函数运行时才能检测到这种禁止情况,会返回一个错误哦(ER_SP_BADSELECT
)
(9)存储例程中使用USE
语句不允许,因为你执行一个例程的时候,隐式的调用了USE db_name
这条指令(并在例程终止时撤消),这个db_name在你创建的时候已经指定好了。如果在引用了非例程所使用的数据库之外的数据库对象,应该指定准确的那个数据库名。
(10)从调用者的SQL mode切换到例程的SQL mode 发生在 参数计算完毕并且作为结果分配给例程参数 之后。如果你的例程创建在strict模式(STRICT_TRANS_TABLES
)下,执行却在nostrict模式下,那么给例程参数分配结果参数的过程就不会发生。如果想在strict模式下分配传递给例程一些表达式,则应调用具有strict模式的例程。
(11)MySQL不会检查确定性是否正确。如果你的例程是不确定的,但是你指定了DETERMINISTIC
参数,可能会影响结果和表现,可能会导致Mysql的优化器执行错误的计划;如果你的例程是确定的,但是你指定了NOT DETERMINISTIC
参数可能会导致优化器不能用而降低了性能。
(12)如果启用了二进制日志记录,则该DETERMINISTIC
特性会影响MySQL是否接受该例程的定义。详情见章节23.7存储程序的二进制日志记录
(13)一个例程可以包含now()
函数或同名函数或rand()
函数,它仍然是复制安全( replication-safe)的?对于 NOW()函数,二进制日志包括时间戳并可以正确复制。 RAND()函数只在执行例程期间只调用一次,也可以正确复制。(可以将例程执行的时间戳和随机数种子视为隐式的输入IN
参数)
(14)如果更改数据库缺省字符集或排序规则,则必须删除并重新创建使用数据库缺省值的存储例程,以便它们使用新的缺省值。
Mysql允许例程 包含DDL(数据定义语言)语句,比如CREATE和DROP,还允许存储过程(不包含存储函数) 使用SQL事物语句比如COMMIT.存储函数不能含有任何包含提交或回滚语句,SQL标准不要求支持这些语句,该标准规定每个DBMS供应商可以决定是否允许它们。