存储过程的使用语法
create PROCEDURE 过程名( in|out|inout 参数名 数据类型 , ...)
begin
sql语句;
end;
call 过程名(参数值);
复制代码
in是定义传入参数的关键字。out是定义出参的关键字。inout是定义一个出入参数都可以的参数。如果括号内什么都不定义,就说明该存储过程时一个无参的函数。在后面会有详细的案例分析。
注意: SQL语句默认的结束符为;,所以在使用以上存储过程时,会报1064的语法错误。我们可以使用DELIMITER关键字临时声明修改SQL语句的结束符为//,如下:
-- 临时定义结束符为"//"
DELIMITER //
create PROCEDURE 过程名( in|out 参数名 数据类型 , ...)
begin
sql语句;
end//
-- 将结束符重新定义回结束符为";"
DELIMITER ;
复制代码例如: 使用存储过程来查询员工的工资(无参)
注意: 如果在特殊的必要情况下,我们还可以通过delimiter关键字将;结束符声明回来使用,在以下案例中我并没有这样将结束符声明回原来的;,在此请大家注意~
为什么我在这里提供了drop(删除)呢?
是因为我们在使用的时候如果需要修改存储过程中的内容,我们需要先删除现有的存储过程后,再creat重新创建。
声明结束符为//
delimiter //
创建存储过程(函数)
create procedure se()
begin
select salary from employee;
end //
调用函数
call se() //
删除已存在存储过程——se()函数
drop procedure if exists se //
复制代码三、存储过程的变量和赋值
3.1 局部变量
声明局部变量语法: declare var_name type [default var_value];
赋值语法:
注意: 局部变量的定义,在begin/end块中有效。
使用set为参数赋值
set赋值
声明结束符为//
delimiter //
创建存储过程
create procedure val_set()
begin
# 声明一个默认值为unknown的val_name局部变量
declare val_name varchar(32) default 'unknown';
# 为局部变量赋值
set val_name = 'Centi';
# 查询局部变量
select val_name;
end //
调用函数
call val_set() //
复制代码使用into接收参数
delimiter //
create procedure val_into()
begin
# 定义两个变量存放name和age
declare val_name varchar(32) default 'unknown';
declare val_age int;
# 查询表中id为1的name和age并放在定义的两个变量中
select name,age into val_name,val_age from employee where id = 1;
# 查询两个变量
select val_name,val_age;
end //
call val_into() //
复制代码3.2 用户变量
用户自定义用户变量,当前会话(连接)有效。与Java中的成员变量相似。
语法: @val_name
注意: 该用户变量不需要提前声明,使用即为声明。
delimiter //
create procedure val_user()
begin
# 为用户变量赋值
set @val_name = 'Lacy';
end //
调用函数
call val_user() //
查询该用户变量
select @val_name //
复制代码3.3 会话变量
会话变量是由系统提供的,只在当前会话(连接)中有效。
语法: @@session.val_name
查看所有会话变量
show session variables;
查看指定的会话变量
select @@session.val_name;
修改指定的会话变量
set @@session.val_name = 0;
复制代码这里我获取了一下所有的会话变量,大概有500条会话变量的记录。等我们深入学习MySQL后,了解了各个会话变量值的作用,可以根据需求和场景来修改会话变量值。
delimiter //
create procedure val_session()
begin
# 查看会话变量
show session variables;
end //
call val_session() //
复制代码
3.4 全局变量
全局变量由系统提供,整个MySQL服务器内有效。
语法: @@global.val_name
查看全局变量中变量名有char的记录
show global variables like '%char%' //
查看全局变量character_set_client的值
select @@global.character_set_client //
复制代码3.5 入参出参
入参出参的语法我们在文章开头已经提过了,但是没有演示,在这里我将演示一下入参出参的使用。
语法: in|out|inout 参数名 数据类型 , ...
in定义出参;out定义入参;inout定义出参和入参。
出参in
使用出参in时,就是需要我们传入参数,在这里可以对参入的参数加以改变。简单来说in只负责传入参数到存储过程中,类似Java中的形参。
delimiter //
create procedure val_in(in val_name varchar(32))
begin
# 使用用户变量出参(为用户变量赋参数值)
set @val_name1 = val_name;
end //
调用函数
call val_in('DK') //
查询该用户变量
select @val_name1 //
复制代码入参out
在使用out时,需要传入一个参数。而这个参数相当于是返回值,可以通过调用、接收来获取这个参数的内容。简单来说out只负责作返回值。
delimiter //
创建一个入参和出参的存储过程
create procedure val_out(in val_id int,out val_name varchar(32))
begin
# 传入参数val_id查询员工返回name值(查询出的name值用出参接收并返回)
select name into val_name from employee where id = val_id;
end //
调用函数传入参数并声明传入一个用户变量
call val_out(1, @n) //
查询用户变量
select @n //
复制代码入参出参inout
inout关键字,就是把in和out合并成了一个关键字使用。被关键字修饰的参数既可以出参也可以入参。
delimiter //
create procedure val_inout(in val_name varchar(32), inout val_age int)
begin
# 声明一个a变量
declare a int;
# 将传入的参数赋值给a变量
set a = val_age;
# 通过name查询age并返回val_age
select age into val_age from employee where name = val_name;
# 将传入的a与-和查询age结果字符串做拼接并查询出来(concat——拼接字符串)
select concat(a, '-', val_age);
end //
声明一个用户变量并赋予参数为40
set @ages = '40' //
调用函数并传入参数值
call val_inout('Ziph', @ages) //
执行结果
40-18
复制代码四、存储过程中的流程控制
4.1 if 条件判断(推荐)
扩展: timestampdiff(unit, exp1, exp2)为exp2 - exp1得到的差值,而单位是unit。(常用于日期)
扩展例子: select timestampdiff(year,’2020-6-6‘,now()) from emp e where id = 1;
解释扩展例子: 查询员工表中id为1员工的年龄,exp2就可以为该员工的出生年月日,并以年为单位计算。
语法:
IF 条件判断 THEN 结果
[ELSEIF 条件判断 THEN 结果] ...
[ELSE 结果]
END IF
复制代码举例: 传入所查询的id参数查询工资标准(s<=6000为低工资标准;6000<s<=10000为中工资标准;10000<s<=15000为中上工资标准;s>=15000为高工资标准)
delimiter //
create procedure s_sql(in val_id int)
begin
# 声明一个局部变量result存放工资标准结果
declare result varchar(32);
# 声明一个局部变量存放查询得到的工资
declare s double;
# 根据入参id查询工资
select salary into s from employee where id = val_id;
# if判断的使用
if s <= 6000 then
set result = '低工资标准';
elseif s <= 10000 then
set result = '中工资标准';
elseif s <= 15000 then
set result = '中上工资标准';
else
set result = '高工资标准';
end if;
# 查询工资标准结果
select result;
end //
调用函数,传入参数
call s_sql(1);
复制代码4.2 case条件判断
关于case语句,不仅仅在存储过程中可以使用,MySQL基础查询语句中也有用到过。相当于是Java中的switch语句。
语法:
语法一
CASE case_value
WHEN when_value THEN 结果
[WHEN when_value THEN 结果] ...
[ELSE 结果]
END CASE
语法二(推荐语法)
CASE
WHEN 条件判断 THEN 结果
[WHEN 条件判断 THEN 结果] ...
[ELSE 结果]
END CASE
复制代码举例:
语法一
delimiter //
create procedure s_case(in val_id int)
begin
# 声明一个局部变量result存放工资标准结果
declare result varchar(32);
# 声明一个局部变量存放查询得到的工资
declare s double;
# 根据入参id查询工资
select salary into s from employee where id = val_id;
case s
when 6000 then set result = '低工资标准';
when 10000 then set result = '中工资标准';
when 15000 then set result = '中上工资标准';
else set result = '高工资标准';
end case;
select result;
end //
call s_case(1);
语法二(推荐)
delimiter //
create procedure s_case(in val_id int)
begin
# 声明一个局部变量result存放工资标准结果
declare result varchar(32);
# 声明一个局部变量存放查询得到的工资
declare s double;
# 根据入参id查询工资
select salary into s from employee where id = val_id;
case
when s <= 6000 then set result = '低工资标准';
when s <= 10000 then set result = '中工资标准';
when s <= 15000 then set result = '中上工资标准';
else set result = '高工资标准';
end case;
select result;
end //
call s_case(1);
复制代码4.3 loop循环
loop为死循环,需要手动退出循环,我们可以使用leave来退出循环
可以把leave看成Java中的break;与之对应的,就有iterate(继续循环)也可以看成Java的continue
语法:
[别名:] LOOP
循环语句
END LOOP [别名]
复制代码注意:别名和别名控制的是同一个标签。
示例1: 循环打印1~10(leave控制循环的退出)
注意:该loop循环为死循环,我们查的110数字是i,在死循环中设置了当大于等于10时停止循环,也就是说先后执行了10次该循环内的内容,结果查询了10次,生成了10个结果(110)。
delimiter //
create procedure s_loop()
begin
# 声明计数器
declare i int default 1;
# 开始循环
num:
loop
# 查询计数器记录的值
select i;
# 判断大于等于停止计数
if i >= 10 then
leave num;
end if;
# 计数器自增1
set i = i + 1;
# 结束循环
end loop num;
end //
call s_loop();
复制代码打印结果:
示例2: 循环打印1~10(iterate和leave控制循环)
注意:这里我们使用字符串拼接计数器结果,而条件如果用iterate就必须时 i < 10 了!
delimiter //
create procedure s_loop1()
begin
# 声明变量i计数器
declare i int default 1;
# 声明字符串容器
declare str varchar(256) default '1';
# 开始循环
num:
loop
# 计数器自增1
set i = i + 1;
# 字符串容器拼接计数器结果
set str = concat(str, '-', i);
# 计数器i如果小于10就继续执行
if i < 10 then
iterate num;
end if;
# 计数器i如果大于10就停止循环
leave num;
# 停止循环
end loop num;
# 查询字符串容器的拼接结果
select str;
end //
call s_loop1();
复制代码
4.4 repeat循环
repeat循环类似Java中的do while循环,直到条件不满足才会结束循环。
语法:
[别名:] REPEAT
循环语句
UNTIL 条件
END REPEAT [别名]
复制代码示例: 循环打印1~10
delimiter //
create procedure s_repeat()
begin
declare i int default 1;
declare str varchar(256) default '1';
# 开始repeat循环
num:
repeat
set i = i + 1;
set str = concat(str, '-', i);
# until 结束条件
# end repeat 结束num 结束repeat循环
until i >= 10 end repeat num;
# 查询字符串拼接结果
select str;
end //
call s_repeat();
复制代码4.5 while循环
while循环就与Java中的while循环很相似了。
语法:
[别名] WHILE 条件 DO
循环语句
END WHILE [别名]
复制代码示例: 循环打印1~10
delimiter //
create procedure s_while()
begin
declare i int default 1;
declare str varchar(256) default '1';
# 开始while循环
num:
# 指定while循环结束条件
while i < 10 do
set i = i + 1;
set str = concat(str, '+', i);
# while循环结束
end while num;
# 查询while循环拼接字符串
select str;
end //
call s_while();
复制代码4.6 流程控制语句(继续、结束)
至于流程控制的继续和结束,我们在前面已经使用过了。这里再列举一下。
leave:与Java中break;相似
leave 标签;
复制代码iterate:与Java中的continue;相似
iterate 标签;
复制代码五、游标与handler
5.1 游标
游标是可以得到某一个结果集并逐行处理数据。游标的逐行操作,导致了游标很少被使用!
语法:
DECLARE 游标名 CURSOR FOR 查询语句
-- 打开语法
OPEN 游标名
-- 取值语法
FETCH 游标名 INTO var_name [, var_name] ...
-- 关闭语法
CLOSE 游标名
复制代码了解了游标的语法,我们开始使用游标。如下:
示例: 使用游标查询id、name和salary。
delimiter //
create procedure f()
begin
declare val_id int;
declare val_name varchar(32);
declare val_salary double;
# 声明游标
declare emp_flag cursor for
select id, name, salary from employee;
# 打开
open emp_flag;
# 取值
fetch emp_flag into val_id, val_name, val_salary;
# 关闭
close emp_flag;
select val_id, val_name, val_salary;
end //
call f();
复制代码执行结果:
因为游标逐行操作的特点,导致我们只能使用游标来查询一行记录。怎么改善代码才可以实现查询所有记录呢?聪明的小伙伴想到了使用循环。对,我们试试使用一下循环。
delimiter //
create procedure f()
begin
declare val_id int;
declare val_name varchar(32);
declare val_salary double;
# 声明游标
declare emp_flag cursor for
select id, name, salary from employee;
# 打开
open emp_flag;
# 使用循环取值
c:loop
# 取值
fetch emp_flag into val_id, val_name, val_salary;
end loop;
# 关闭
close emp_flag;
select val_id, val_name, val_salary;
end //
call f();
复制代码
我们使用循环之后,发现有一个问题,因为循环是死循环,我们不加结束循环的条件,游标会一直查询记录,当查到没有的记录的时候,就会抛出异常1329:未获取到选择处理的行数。
如果我们想办法指定结束循环的条件该怎么做呢?
这时候可以声明一个boolean类型的标记。如果为true时则查询结果集,为false时则结束循环。
delimiter //
create procedure f()
begin
declare val_id int;
declare val_name varchar(32);
declare val_salary double;
# 声明flag标记
declare flag boolean default true;
# 声明游标
declare emp_flag cursor for
select id, name, salary from employee;
# 打开
open emp_flag;
# 使用循环取值
c:loop
fetch emp_flag into val_id, val_name, val_salary;
# 如果标记为true则查询结果集
if flag then
select val_id, val_name, val_salary;
# 如果标记为false则证明结果集查询完毕,停止死循环
else
leave c;
end if;
end loop;
# 关闭
close emp_flag;
select val_id, val_name, val_salary;
end //
call f();
复制代码上述代码你会发现并没有写完,它留下了一个很严肃的问题。当flag = false时候可以结束循环。但是什么时候才让flag为false啊?
于是,MySQL为我们提供了一个handler句柄。它可以帮我们解决此疑惑。
handler句柄语法: declare continue handler for 异常 set flag = false;
handler句柄可以用来捕获异常,也就是说在这个场景中当捕获到1329:未获取到选择处理的行数时,就将flag标记的值改为false。这样使用handler句柄就解决了结束循环的难题。让我们来试试吧!
终极版示例: 解决了多行查询以及结束循环问题。
delimiter //
create procedure f()
begin
declare val_id int;
declare val_name varchar(32);
declare val_salary double;
# 声明flag标记
declare flag boolean default true;
# 声明游标
declare emp_flag cursor for
select id, name, salary from employee;
# 使用handler句柄来解决结束循环问题
declare continue handler for 1329 set flag = false;
# 打开
open emp_flag;
# 使用循环取值
c:loop
fetch emp_flag into val_id, val_name, val_salary;
# 如果标记为true则查询结果集
if flag then
select val_id, val_name, val_salary;
# 如果标记为false则证明结果集查询完毕,停止死循环
else
leave c;
end if;
end loop;
# 关闭
close emp_flag;
select val_id, val_name, val_salary;
end //
call f();
复制代码执行结果:
在执行结果中,可以看出查询结果以多次查询的形式,分布显示到了每一个查询结果窗口中。
注意: 在语法中,变量声明、游标声明、handler声明是必须按照先后顺序书写的,否则创建存储过程出错。
5.2 handler句柄
语法:
DECLARE handler操作 HANDLER
FOR 情况列表...(比如:异常错误情况)
操作语句
复制代码注意:异常情况可以写异常错误码、异常别名或SQLSTATE码。
handler操作:
CONTINUE: 继续
EXIT: 退出
UNDO: 撤销
异常情况列表:
mysql_error_code
SQLSTATE [VALUE] sqlstate_value
condition_name
SQLWARNING
NOT FOUND
SQLEXCEPTION
注意: MySQL中各种异常情况代码、错误码、别名和SQLSTATEM码可参考官方文档:
dev.mysql.com/doc/refman/…
写法示例:
DECLARE exit HANDLER FOR SQLSTATE '3D000' set flag = false;
DECLARE continue HANDLER FOR 1050 set flag = false;
DECLARE continue HANDLER FOR not found set flag = false;
复制代码六、循环创建表
需求: 创建下个月的每天对应的表,创建的表格式为:comp_2020_06_01、comp_2020_06_02、...
描述: 我们需要用某个表记录很多数据,比如记录某某用户的搜索、购买行为(注意,此处是假设用数据库保存),当每天记录较多时,如果把所有数据都记录到一张表中太庞大,需要分表,我们的要求是,每天一张表,存当天的统计数据,就要求提前生产这些表——每月月底创建下一个月每天的表!
预编译: PREPARE 数据库对象名 FROM 参数名
执行: EXECUTE 数据库对象名 [USING @var_name [, @var_name] ...]
通过数据库对象创建或删除表: {DEALLOCATE | DROP} PREPARE 数据库对象名
关于时间处理的语句:
-- EXTRACT(unit FROM date) 截取时间的指定位置值
-- DATE_ADD(date,INTERVAL expr unit) 日期运算
-- LAST_DAY(date) 获取日期的最后一天
-- YEAR(date) 返回日期中的年
-- MONTH(date) 返回日期的月
-- DAYOFMONTH(date) 返回日
复制代码代码:
-- 思路:循环构建表名 comp_2020_06_01 到 comp_2020_06_30;并执行create语句。
delimiter //
create procedure sp_create_table()
begin
# 声明需要拼接表名的下一个月的年、月、日
declare next_year int;
declare next_month int;
declare next_month_day int;
# 声明下一个月的月和日的字符串
declare next_month_str char(2);
declare next_month_day_str char(2);
# 声明需要处理每天的表名
declare table_name_str char(10);
# 声明需要拼接的1
declare t_index int default 1;
# declare create_table_sql varchar(200);
# 获取下个月的年份
set next_year = year(date_add(now(),INTERVAL 1 month));
# 获取下个月是几月
set next_month = month(date_add(now(),INTERVAL 1 month));
# 下个月最后一天是几号
set next_month_day = dayofmonth(LAST_DAY(date_add(now(),INTERVAL 1 month)));
# 如果下一个月月份小于10,就在月份的前面拼接一个0
if next_month < 10
then set next_month_str = concat('0',next_month);
else
# 如果月份大于10,不做任何操作
set next_month_str = concat('',next_month);
end if;
# 循环操作(下个月的日大于等于1循环开始循环)
while t_index <= next_month_day do
# 如果t_index小于10就在前面拼接0
if (t_index < 10)
then set next_month_day_str = concat('0',t_index);
else
# 如果t_index大于10不做任何操作
set next_month_day_str = concat('',t_index);
end if;
# 拼接标命字符串
set table_name_str = concat(next_year,'_',next_month_str,'_',next_month_day_str);
# 拼接create sql语句
set @create_table_sql = concat(
'create table comp_',
table_name_str,
'(`grade` INT(11) NULL,`losal` INT(11) NULL,`hisal` INT(11) NULL) COLLATE=\'utf8_general_ci\' ENGINE=InnoDB');
# 预编译
# 注意:FROM后面不能使用局部变量!
prepare create_table_stmt FROM @create_table_sql;
# 执行
execute create_table_stmt;
# 创建表
DEALLOCATE prepare create_table_stmt;
# t_index自增1
set t_index = t_index + 1;
end while;
end//
调用函数
call sp_create_table()
复制代码七、其他
7.1 characteristic
在MySQL存储过程中,如果没有显示的定义characteristic,它会隐式的定义一系列特性的默认值来创建存储过程。
LANGUAGE SQL
存储过程语言,默认是sql,说明存储过程中使用的是sql语言编写的,暂时只支持sql,后续可能会支持其他语言
NOT DETERMINISTIC
是否确定性的输入就是确定性的输出,默认是NOT DETERMINISTIC,只对于同样的输入,输出也是一样的,当前这个值还没有使用
CONTAINS SQL
提供子程序使用数据的内在信息,这些特征值目前提供给服务器,并没有根据这些特征值来约束过程实际使用数据的情况。有以下选择:
CONTAINS SQL表示子程序不包含读或者写数据的语句
NO SQL 表示子程序不包含sql
READS SQL DATA 表示子程序包含读数据的语句,但是不包含写数据的语句
MODIFIES SQL DATA 表示子程序包含写数据的语句。
SQL SECURITY DEFINER
MySQL存储过程是通过指定SQL SECURITY子句指定执行存储过程的实际用户。所以次值用来指定存储过程是使用创建者的许可来执行,还是执行者的许可来执行,默认值是DEFINER
DEFINER 创建者的身份来调用,对于当前用户来说:如果执行存储过程的权限,且创建者有访问表的权限,当前用户可以成功执行过程的调用的
INVOKER 调用者的身份来执行,对于当前用户来说:如果执行存储过程的权限,以当前身份去访问表,如果当前身份没有访问表的权限,即便是有执行过程的权限,仍然是无法成功执行过程的调用的。
COMMENT ''
存储过程的注释性信息写在COMMENT里面,这里只能是单行文本,多行文本会被移除到回车换行等
7.2 死循环处理
如有死循环处理,可以通过下面的命令查看并杀死(结束)
show processlist;
kill id;
复制代码7.3 select语句中书写case
select
case
when 条件判断 then 结果
when 条件判断 then 结果
else 结果
end 别名,
*
from 表名;
复制代码7.4 复制表和数据
CREATE TABLE dept SELECT * FROM procedure_demo.dept;
CREATE TABLE emp SELECT * FROM procedure_demo.emp;
CREATE TABLE salgrade SELECT * FROM procedure_demo.salgrade;
复制代码7.5 临时表
create temporary table 表名(
字段名 类型 [约束],
name varchar(20)
)Engine=InnoDB default charset utf8;
-- 需求:按照部门名称查询员工,通过select查看员工的编号、姓名、薪资。(注意,此处仅仅演示游标用法)
delimiter ?
create procedure sp_create_table02(in dept_name varchar(32))
begin
declare emp_no int;
declare emp_name varchar(32);
declare emp_sal decimal(7,2);
declare exit_flag int default 0;
declare emp_cursor cursor for
select e.empno,e.ename,e.sal
from emp e inner join dept d on e.deptno = d.deptno where d.dname = dept_name;
declare continue handler for not found set exit_flag = 1;
-- 创建临时表收集数据
CREATE temporary TABLE `temp_table_emp` (
`empno` INT(11) NOT NULL COMMENT '员工编号',
`ename` VARCHAR(32) NULL COMMENT '员工姓名' COLLATE 'utf8_general_ci',
`sal` DECIMAL(7,2) NOT NULL DEFAULT '0.00' COMMENT '薪资',
PRIMARY KEY (`empno`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
open emp_cursor;
c_loop:loop
fetch emp_cursor into emp_no,emp_name,emp_sal;
if exit_flag != 1 then
insert into temp_table_emp values(emp_no,emp_name,emp_sal);
else
leave c_loop;
end if;
end loop c_loop;
select * from temp_table_emp;
select @sex_res; -- 仅仅是看一下会不会执行到
close emp_cursor;
end?
call sp_create_table02('RESEARCH');
mysql中isnull,ifnull,nullif的用法如下:
isnull :存在 為0, 不存在為1
nullif(expr1,expr2):expr1=expr2 則null,expr1!=expr2 則expr1
ifnull(expr1,expr2):expr1存在 為expr1, 不存在為expr2
怎麼根據名稱和時間查詢第一個
Select * fron t WHERE NAME=aname AND j!=c order BY jdate ASC LIMIT 1;
怎麼使用表的別名
Select *,a.j fron t a
Select *,a.j fron t as a
根据查询结果创建临时表
CREATE TEMPORARY TABLE temp_a SELECT * FROM t
mysql 多行数据转换xml
利用mysql 中 CONCAT和 GROUP_CONCAT
GROUP_CONCAT 默认是使用逗号分割的数据,需要使用SEPARATOR 去重新指定
直接上案例:
select CONCAT('<chdlist>',GROUP_CONCAT(CONCAT('<chd>',
'<ch_id>',ch_id,'</ch_id>',
'<ch_name>',REPLACE(ch_name,'>','>') ,'</ch_name>'
'<ch_price>',ch_price,'</ch_price>'
'<ch_number>',ch_number,'</ch_number>'
'<pr_cost>',pr_cost,'</pr_cost>'
'<ch_pd>',ch_pd,'</ch_pd>'
),'</chd>' SEPARATOR ''),'</chdlist>') from chpro;
简要说明:
SEPARATOR '' 把逗号修改成空
CONCAT('xml父节点开始',GROUP_CONCAT('子节点开始','列1名称',列1,'列1名称','列2名称',列2,'列2名称','子节点结束' SEPARATOR ''),'xml父节点结束')
注意:GROUP_CONCAT 是有长度限制的。需要在my.ini或my-default.ini 中设置长度group_concat_max_len=200000
mysql 获取当前日期及格式化
MYSQL 获取当前日期及日期格式
获取系统日期: NOW()
格式化日期: DATE_FORMAT(date, format)
注: date:时间字段
format:日期格式
返回系统日期,输出 2009-12-25 14:38:59
select now();
输出 09-12-25
select date_format(now(),'%y-%m-%d');
根据format字符串格式化date值:
%S, %s 两位数字形式的秒( 00,01, ..., 59)
%I, %i 两位数字形式的分( 00,01, ..., 59)
%H 两位数字形式的小时,24 小时(00,01, ..., 23)
%h 两位数字形式的小时,12 小时(01,02, ..., 12)
%k 数字形式的小时,24 小时(0,1, ..., 23)
%l 数字形式的小时,12 小时(1, 2, ..., 12)
%T 24 小时的时间形式(hh:mm:ss)
%r 12 小时的时间形式(hh:mm:ss AM 或hh:mm:ss PM)
%p AM或PM
%W 一周中每一天的名称(Sunday, Monday, ..., Saturday)
%a 一周中每一天名称的缩写(Sun, Mon, ..., Sat)
%d 两位数字表示月中的天数(00, 01,..., 31)
%e 数字形式表示月中的天数(1, 2, ..., 31)
%D 英文后缀表示月中的天数(1st, 2nd, 3rd,...)
%w 以数字形式表示周中的天数( 0 = Sunday, 1=Monday, ..., 6=Saturday)
%j 以三位数字表示年中的天数( 001, 002, ..., 366)
%U 周(0, 1, 52),其中Sunday 为周中的第一天
%u 周(0, 1, 52),其中Monday 为周中的第一天
%M 月名(January, February, ..., December)
%b 缩写的月名( January, February,...., December)
%m 两位数字表示的月份(01, 02, ..., 12)
%c 数字表示的月份(1, 2, ...., 12)
%Y 四位数字表示的年份
%y 两位数字表示的年份
%% 直接值“%”
MySQL server has gone away 問題的解決方法(查詢的結果集超過 max_allowed_packet )
MYSQL教程 • 發表 2018-10-08
mysql出現ERROR : (2006, 'MySQL server has gone away') 的問題意思就是指client和MySQL server之間的連結斷開了。
造成這樣的原因一般是sql操作的時間過長,或者是傳送的資料太大(例如使用insert ... values的語句過長, 這種情況可以通過修改max_allowed_packed的配置引數來避免,也可以在程式中將資料分批插入)。
產生這個問題的原因有很多,總結下網上的分析:
原因一. MySQL 服務宕了
判斷是否屬於這個原因的方法很簡單,進入mysql控制檯,檢視mysql的執行時長
mysql> show global status like 'uptime';
+---------------+---------+
| Variable_name | Value |
+---------------+---------+
| Uptime | 3414707 |
+---------------+---------+
1 row in set或者檢視MySQL的報錯日誌,看看有沒有重啟的資訊
如果uptime數值很大,表明mysql服務運行了很久了。說明最近服務沒有重啟過。
如果日誌沒有相關資訊,也表名mysql服務最近沒有重啟過,可以繼續檢查下面幾項內容。
原因二. mysql連線超時
即某個mysql長連線很久沒有新的請求發起,達到了server端的timeout,被server強行關閉。
此後再通過這個connection發起查詢的時候,就會報錯server has gone away
(大部分PHP指令碼就是屬於此類)
mysql> show global variables like '%timeout';
+----------------------------+----------+
| Variable_name | Value |
+----------------------------+----------+
| connect_timeout | 10 |
| delayed_insert_timeout | 300 |
| innodb_lock_wait_timeout | 50 |
| innodb_rollback_on_timeout | OFF |
| interactive_timeout | 28800 |
| lock_wait_timeout | 31536000 |
| net_read_timeout | 30 |
| net_write_timeout | 60 |
| slave_net_timeout | 3600 |
| wait_timeout | 28800 |
+----------------------------+----------+
10 rows in set
wait_timeout 是28800秒,即mysql連結在無操作28800秒後被自動關閉
原因三. mysql請求連結程序被主動kill
這種情況和原因二相似,只是一個是人為一個是MYSQL自己的動作
mysql> show global status like 'com_kill';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_kill | 21 |
+---------------+-------+
1 row in set原因四. Your SQL statement was too large.
當查詢的結果集超過 max_allowed_packet 也會出現這樣的報錯。定位方法是打出相關報錯的語句。
用select * into outfile 的方式匯出到檔案,檢視檔案大小是否超過 max_allowed_packet ,如果超過則需要調整引數,或者優化語句。
mysql> show global variables like 'max_allowed_packet';
+--------------------+---------+
| Variable_name | Value |
+--------------------+---------+
| max_allowed_packet | 1048576 |
+--------------------+---------+
1 row in set (0.00 sec)
修改引數:
mysql> set global max_allowed_packet=1024102416;
mysql> show global variables like 'max_allowed_packet';
+--------------------+----------+
| Variable_name | Value |
+--------------------+----------+
| max_allowed_packet | 16777216 |
+--------------------+----------+
1 row in set (0.00 sec)
SET @@global.max_allowed_packet = 10010241024;
show global variables like 'max_allowed_packet';
查看栏位数据大小(M)
select concat(round(sum(LENGTH(XML)/1024/1024),2),'M')
from xmlintest WHERE ID=3;
基于SQL中SET与SELECT赋值的区别详解
最近的项目写的SQL比较多,经常会用到对变量赋值,而我使用SET和SELECT都会达到效果。
那就有些迷惑,这两者有什么区别呢?什么时候哪该哪个呢?
经过网上的查询,及个人练习,总结两者有以下几点主要区别:
假定有设定变量:
复制代码代码如下:
DECLARE @VAR1 VARCHAR(1)
DECLARE @VAR2 VARCHAR(2)
1、SELECT可以在一条语句里对多个变量同时赋值,而SET只能一次对一个变量赋值,如下:
复制代码代码如下:
SELECT @VAR1='Y',@VAR2='N'
-- 而SET要达到同样的效果,需要:
SET @VAR1='Y'
SET @VAR2='N'
/*
说到这个,SQL内置的变量:@@ERROR 和 @@ROWCOUNT必须要在一句SQL语句中捕获。如果用set分两句来获取它们,将获取不完整,这时就应该用select来获取值。
/
2、表达式返回多个值时,用SET将会出错,而SELECT将取最后一个值,如下:
复制代码代码如下:
----以下假定Permission表有多个IsRight记录
SELECT @VAR1 = IsRight FROM Permission --将取最后一个值
SET @VAR1 = IsRight FROM Permission --将报错
3、表达式无返回值时,用SET将置变量值为NULL,用SELECT交保持变量值,如下:
复制代码代码如下:
----以下假定Permission记录为空
SET @VAR1 = '初始值'
SELECT @VAR1 = IsRight FROM Permission --此时@VAR1为'初始值'
SET @VAR1 = (SELECT IsRight FROM Permission) --此时@VAR1为NULL
4、使用标量子查询时,如果无返回值,SET和SELECT一样,都将置为NULL,如下:
复制代码代码如下:
----以下假定Permission记录为空
SET @VAR1 = '初始值'
SELECT @VAR1 =(SELECT IsRight FROM Permission ) --此时@VAR1为NULL
SET @VAR1 = ( SELECT IsRight FROM Permission) --此时@VAR1为NULL
SELECT VERSION(), CURRENT_DATE; 查看mysql版本號,當前日期
select user();查看登入用戶;
SHOW DATABASES; 查看所有數據庫
CREATE DATABASE menagerie;创建数据库menagerie
Use menagerie;使用menagerie数据库
DESCRIBE pet;描述 pet表
要将文本文件pet.txt加载到pet table 中,请使用以下语句:
LOAD DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet;
如果您是使用\r\n作为行终止符的编辑器在 Windows 上创建的文件,则应改用以下语句:
mysql> LOAD DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet
LINES TERMINATED BY '\r\n';
TIMESTAMPDIFF(YEAR,birth,CURDATE())計算年齡
由于与NULL进行任何算术比较的结果均为NULL,(“”)不是null, null=null
create DATABASE mysqlzw1;
CREATE USER 'userZW'@'localhost' IDENTIFIED BY 'mysqlzw1';
GRANT ALL PRIVILEGES ON newdatabase. TO ' userZW'@'localhost';
FLUSH PRIVILEGES;