[1]
存储过程,函数,触发器,及区别:
区别:
1)
trigger与producer,function的区别:
触发器能够自动执行并且不含有参数.
2)
producer,function的区别
1,必须要一个RETURNS语句来定义返回值类型
2,不能指定参数的IN、OUT或INOUT修饰符,所有参数隐式的为IN
3,Function体必须包含RETURN语句来终结Function执行并返回指定的结果给调用者
1.
触发器:
Trigger是数据库中的事件触发,当前MySQL的实现是对特定table的DML语句(INSERT/UPDATE/DELETE)调用时触发.格式:
CREATE [DEFINER={user|CURRENT_USER}] TRIGGER trigger_name
{BEFORE|AFTER}
{INSERT|UPDATE|DELETE}
ON table_name
FOR EACH ROW
trigger_statements
查看触发器的命令:
show triggers ;
也可用 show triggers \G
实例:
触发器的作用是: 用于管理数据库中有关系的表.
/*先删除将要创建而存在的表*/
drop table if exists Student;
drop table if exists BorrowStudent;
/*创建表*/
create table Student(
StudentID int not null primary key,
StudentName varchar(30) not null,
StudentSex enum('m','f') default 'm'
)engine=myisam;
create table BorrowStudent(
BorrowRecord int not null auto_increment primary key,
StudentID int not null,
BorrorDate date,
ReturnDate date,
foreign key(StudentID) references Student(StudentID)
)engine=myisam;
/*插入记录*/
insert into Student values(1235412,'java','m');
insert into Student values(3214562,'jiajia','m');
insert into Student values(5441253,'purana','f');
insert into BorrowStudent(StudentID,BorrorDate,ReturnDate)
values(1235412,'2007-01-01','2007-01-07');
insert into BorrowStudent(StudentID,BorrorDate,ReturnDate)
values(3214562,'2007-01-01','2007-01-07');
insert into BorrowStudent(StudentID,BorrorDate,ReturnDate)
values(5441253,'2007-01-01','2007-01-07');
/*创建触发器*/
delimiter $$
drop trigger if exists tduStudent$$
drop trigger if exists tddStudent$$
create trigger tduStudent before update
on Student for each row
begin
/*
new.触发器作用表的某字段名:表示修改后的字段值;
old.触发器作用表的某字段名:表示修改前的字段值;
*/
if new.StudentID!=old.StudentID then -- 意思是:当表studentID发生变化时,执行
update BorrowStudent
set BorrowStudent.StudentID=new.StudentID
where BorrowStudent.StudentID=old.StudentID;
end if;
end$$
create trigger tddStudent before delete
on Student for each row
begin
delete
from BorrowStudent
where BorrowStudent.StudentID=old.StudentID;
end$$
delimiter ;
2.
函数function:
在命令行查看方式:
show function status;
show create function ***;
在命令行调用方法:
select fun_sum(1) //
实例一:
delimiter //
drop function if exists fun_sum //
create function fun_sum(userid double)
returns double
begin
declare ta double;
select sum(totalamount) into ta from customer where ctype=userid;
set ta=ta+10000;
return ta;
end //
命令行调用时,用:select fun_sum(1) //
可在select / insert /update /delete中调用函数。如下: ---->比存储过程强的地方!!!
insert into customer values(3,fun_sum(2))//
update customer set ctype=4 where totalamount=fun_sum(2);
关于function中的insert/update/delete:
函数中,也可以有insert,update,delete这些操作,只能用命令行方式:select function()进行调用.
若要用DML(select/insert/update/delete)中调用函数,则函数中不可以有insert/update/delete等操作.
mysql中存在很多内建函数.如:
IFNULL
value = IFNULL(value, nullvalue)
如果value不为NULL则返回value,否则返回第二个参数nullvalue
FORMAT
string = FORMAT(number, dal_places)
使用指定的decimal_place小数位来格式化number,并在每个千位上加上逗号来进行分隔.
3.
存储过程是数据库服务器端的一段程序.
什么时候需要用存储过程
存储过程通常是一些经常要执行的任务,这些任务往往是针对大量的记录而进行的。在服务器上执行存储过程,可以改善应用程序的性能。这是因为:
1)
服务器往往具有强大的计算能力和速度。
2)
避免把大量的数据下载到客户端,减少网络上的传输量。
3)
存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。
4)
安全性高,可设定只有某此用户才具有对指定存储过程的使用权 (如何设置???)
存储过程名对大小写不敏感,因此‘P1’和‘p1’是同一个名字,可以采取“数据库名.存储过程名”来区分.
4.1
创建存储过程时,先用选择相应的数据库。因为创建的存储过程是属于某一数据库的。
4.2
调用存储过程:
基本语法:call sp_name()
注意:存储过程名称后面必须加括号,哪怕该存储过程没有参数传递
如下:
call p1 () //
4.3
删除存储过程
1.基本语法:
drop procedure sp_name//
注意事项:
不能在一个存储过程中删除另一个存储过程,只能调用另一个存储过程
4.4
存储过程的查看:
show procedure status// -->显示所有的存储过程
show create procedure 数据库名.sp_name \G -->显示某一存储过程
存储过程中的判断,循环:
case的用法:
CREATE PROCEDURE p13 (IN parameter1 INT)
BEGIN
DECLARE variable1 INT;
SET variable1 = parameter1 + 1;
CASE variable1
WHEN 0 THEN INSERT INTO t VALUES (17);
WHEN 1 THEN INSERT INTO t VALUES (18);
ELSE INSERT INTO t VALUES (19);
END CASE;
END; //
case中SQL中的应用: -->判断也可以有if ...then ...else...end if
case
when $P{codeFrom}!='ALL' then
id>=(select id from im_item_definition where item_code=$P{codeFrom})
else
1=1
end
while...end while的用法:
CREATE PROCEDURE p14 ()
BEGIN
DECLARE v INT;
SET v = 0;
WHILE v < 5 DO
INSERT INTO t VALUES (v);
SET v = v + 1;
END WHILE;
repeat ...end repeat的用法:
REPEAT
INSERT INTO t VALUES (v);
SET v = v + 1;
UNTIL v >= 5 --->这里可以加上";",也可不用
END REPEAT;
END; //
这是一个REPEAT循环的例子,功能和前面WHILE循环一样。区别在于repeat在执行后进行检查,而WHILE则是执行前检查.
END; //
存储过程的注释:
多行注释,用"/**/"
单行注释,用"--",须注意."--"后,须至少一个空格.
备份存储过程:
mysqldump -uroot -R procedureDemo > /root/backup.sql
说明:
-R,表示在导出时,导出存储过程以及自定义函数。
在java中调用存储过程:
mysql> create procedure sumPrice(nameType int,out totalAmount double)
-> begin
-> declare name varchar(32);
-> if nameType=0 then -- 不能用"==0"
-> set name= 'all';
-> select sum(price*qty) into totalAmount from price;
-> insert into Amount values(name,totalAmount);
-> else
-> select customer into name from price where id=nameType;
-> select sum(price*qty) into totalAmount from price where id=nameType;
-> insert into Amount values(name,totalAmount);
-> end if;
-> end//
Query OK, 0 rows affected (0.08 sec)
代码:
double results=0;
CallableStatement cstmt=null;
String sql="{call sumPrice(?,?)}";
try {
cstmt=con.prepareCall(sql);
cstmt.setInt(1, customerid); //若类型是string,则用setString(1,**)
cstmt.registerOutParameter(2,Types.DOUBLE);
cstmt.execute();
results=cstmt.getDouble(2);
} catch (SQLException e) {
e.printStackTrace();
}
游标:
定义: DECLARE cursor_name CURSOR FOR SELECT_statement; -->要放于最前面的声明中.
打开游标: OPEN cursor_name;
使用循环语句获取: FETCH cursor_name INTO variable list;
关闭游标: CLOSE cursor_name ;
应用实例:
create procedure showAll()
begin
declare a,b,c int default 0;
declare name default 'jason'; -- 若为中文,则要加引号.
declare cur cursor for select price,qty from price;
declare exit handler for not found set c=1;
create temporary table temp1(price double,qty double,jdate varchar(255));
open cur;
repeat
fetch cur into a,b;
insert into temp1 values(a,b,now());
until c=1 -- 不能有";"
end repeat;
close cur;
end//
存储过程的调试:
查看存储过程中变量的值,可以将变量的值赋给用户变量.如:
set @x=v_end;
执行后存储过程后,可以用select @x;进行查看.
存储过程中调用其它的存储过程--->好用:一个存储过程可以在另一存储过程的被调用,使原来的 存储过程的代码比较简洁,且封装,如java一样:
调用时,如:
.......
call trend_scm.jBPIdChange(v_bpcode);
.......
存储过程的结构可以是:
drop procedure if exists trend_scm.jICActionPostAC //
create procedure jICActionPostAC(actionid double,out isSucess int)
begin
-- define the variables
declare isSucess int default 0;
..........声明区域.............
-- define exception is very important.
declare exit handler for SQLException rollback; --然后再结合下面的start transaction,commit
.........
set isSucess=0; --->当存储过程执行成功后,在JAVA中获取可得isSuccess=1.让程序知道成功
start transaction;
begin --------->把各处理逻辑放入各自的子模块中,这样就不会冲突.
子模块1
end;
begin
子模块2
end;
commit;
set isSucess=1; -- 设置返回值
end //
[2]外键与索引:
1.
外键:
作用:
让数据库自己通过外键来保证数据的完整性和一致性;
同时,引入外键会使速度和性能下降。
添加外键的格式:
ALTER TABLE yourtablename
ADD [CONSTRAINT 外键名] FOREIGN KEY [id] (index_col_name, ...)
REFERENCES tbl_name (index_col_name, ...)
[ON DELETE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
[ON UPDATE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
注意:
创建外键时,定义外键名时,不能加引号.
如: constraint 'fk_1' 或 constraint "fk_1"是错误的
说明:
on delete/on update,用于定义delete,update操作.以下是update,delete操作的各种约束类型:
CASCADE:
外键表中外键字段值会被更新,或所在的列会被删除.
RESTRICT:
RESTRICT也相当于no action,即不进行任何操作.即,拒绝父表update外键关联列,delete记录.
set null:
被父面的外键关联字段被update ,delete时,子表的外键列被设置为null.
而对于insert,子表的外键列输入的值,只能是父表外键关联列已有的值.否则出错.
外键定义的前提条件:
1)
所有tables必须是InnoDB型,它们不能是临时表.因为在MySQL中只有InnoDB类型的表才支持外键.
2)
所有要建立外键的字段必须建立索引.
3)
对于非InnoDB表,FOREIGN KEY子句会被忽略掉。
查看外键:
SHOW CREATE TABLE ***;可以查看到新建的表的代码以及其存储引擎.也就可以看到外键的设置.
删除外键:
alter table drop foreign key '外键名'.
注意:
只有在定义外键时,用constraint 外键名 foreign key .... 方便进行外键的删除.
实例一:
CREATE TABLE parent(id INT NOT NULL,PRIMARY KEY (id)) TYPE=INNODB;
-- type=innodb 相当于 engine=innodb
CREATE TABLE child(id INT, parent_id INT,INDEX par_ind (parent_id),
FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE
) TYPE=INNODB;
向parent插入数据后,向child插入数据,插入时,child中的parent_id的值只能是parent中有的数据,否则插入不成功;
删除parent记录时,child中的相应记录也会被删除;-->因为: on delete cascade
更新parent记录时,不给更新;-->因为没定义,默认采用restrict.
子表也可这样定义:
create table child(id int not null primary key auto_increment,parent_id int,
index par_ind (parent_id),
constraint fk_1 foreign key (parent_id) references
parent(id) on update cascade on delete restrict)
type=innodb;
最后说明:
1.若不声明on update/delete,则默认是采用restrict方式.
2.对于外键约束,最好是采用: ON UPDATE CASCADE ON DELETE RESTRICT 的方式.
3.对于多外键,只是在一个外键的基础上添加另一索引与外键.
索引:
若表数据越多,若进行多表查询,索引的作用体现得越明显.
索引分类: 普通索引、UNIQUE索引或PRIMARY KEY索引。
创建索引:
ALTER TABLE table_name ADD INDEX index_name (column_list);
ALTER TABLE table_name ADD UNIQUE (column_list);
ALTER TABLE table_name ADD PRIMARY KEY (column_list);
或
CREATE INDEX index_name ON table_name (column_list)
CREATE UNIQUE INDEX index_name ON table_name (column_list)
删除索引:
ALTER TABLE table_name DROP INDEX index_name
或
DROP INDEX index_name ON talbe_name
[3]
SQL的技巧:
not exists/exists;
union/union all;
(select * from table) a;
where,group by ,having;
-----------------------------------数据库编程题---------------------------------------
[0]
实例一(我的面试题):--------->important
有以下三表:
S(sid,sname)
C(cid,cname)
SC(sid,cid)
查询要求:查询出选课数大于8(不包括8)的选课学生的ID,及名称,以及选课的总数.同时,以选课总数降序排序;若选课总数相同,按学生ID排序.
实现如下:
select s.sid,s.sname,a.num from sc,s,(select cid,count(*) num from sc group by cid having num>8) a where a.cid=sc.cid and sc.sid=s.sid order by a.num desc,s.sid;
关键点:
1.
把查询结果作为临时表,有对列值的获取(a.num) + group by having的应用:
(select cid,count(*) num from sc group by cid having num>8) a
2.
以选课总数降序排序;若选课总数相同,按学生ID排序.(其实就是一级排序与二级排序)
其实就是:
order by a.num desc , s.sid;
[1]一道SQL语句面试题,关于group by
表内容:
2005-05-09 胜
2005-05-09 胜
2005-05-09 负
2005-05-09 负
2005-05-10 胜
2005-05-10 负
2005-05-10 负
如果要生成下列结果, 该如何写sql语句?
胜 负
2005-05-09 2 2
2005-05-10 1 2
------------------------------------------
create table #tmp(rq varchar(10),shengfu nchar(1))
insert into #tmp values('2005-05-09','胜')
insert into #tmp values('2005-05-09','胜')
insert into #tmp values('2005-05-09','负')
insert into #tmp values('2005-05-09','负')
insert into #tmp values('2005-05-10','胜')
insert into #tmp values('2005-05-10','负')
insert into #tmp values('2005-05-10','负')
1)select rq, sum(case when shengfu='胜' then 1 else 0 end)'胜',sum(case when shengfu='负' then 1 else 0 end)'负' from #tmp group by rq
2) select N.rq,N.��,M.� from (
select rq,��=count(*) from #tmp where shengfu='胜'group by rq)N inner join
(select rq,�=count(*) from #tmp where shengfu='负'group by rq)M on N.rq=M.rq
3)select a.col001,a.a1 胜,b.b1 负 from
(select col001,count(col001) a1 from temp1 where col002='胜' group by col001) a,
(select col001,count(col001) b1 from temp1 where col002='负' group by col001) b
where a.col001=b.col001
[2].请教一个面试中遇到的SQL语句的查询问题
表中有A B C三列,用SQL语句实现:当A列大于B列时选择A列否则选择B列,当B列大于C列时选择B列否则选择C列。
------------------------------------------
select (case when a>b then a else b end ),
(case when b>c then b esle c end)
from table_name
[3].面试题:一个日期判断的sql语句?
请取出tb_send表中日期(SendTime字段)为当天的所有记录?(SendTime字段为datetime型,包含日期与时间)
------------------------------------------
select * from tb where datediff(dd,SendTime,getdate())=0
[4].有一张表,里面有3个字段:语文,数学,英语。其中有3条记录分别表示语文70分,数学80分,英语58分,请用一条sql语句查询出这三条记录并按以下条件显示出来(并写出您的思路):
大于或等于80表示优秀,大于或等于60表示及格,小于60分表示不及格。
显示格式:
语文 数学 英语
及格 优秀 不及格
------------------------------------------
select
(case when 语文>=80 then '优秀'
when 语文>=60 then '及格'
else '不及格') as 语文,
(case when 数学>=80 then '优秀'
when 数学>=60 then '及格'
else '不及格') as 数学,
(case when 英语>=80 then '优秀'
when 英语>=60 then '及格'
else '不及格') as 英语,
from table
[5]
请用一个sql语句得出结果
从table1,table2中取出如table3所列格式数据,注意提供的数据及结果不准确,只是作为一个格式向大家请教。
如使用存储过程也可以。
table1
月份mon 部门dep 业绩yj
-------------------------------
一月份 01 10
一月份 02 10
一月份 03 5
二月份 02 8
二月份 04 9
三月份 03 8
table2
部门dep 部门名称dname
--------------------------------
01 国内业务一部
02 国内业务二部
03 国内业务三部
04 国际业务部
table3 (result)
部门dep 一月份 二月份 三月份
--------------------------------------
01 10 null null
02 10 8 null
03 null 5 8
04 null null 9
------------------------------------------
1)
select a.部门名称dname,b.业绩yj as '一月份',c.业绩yj as '二月份',d.业绩yj as '三月份'
from table1 a,table2 b,table2 c,table2 d
where a.部门dep = b.部门dep and b.月份mon = '一月份' and
a.部门dep = c.部门dep and c.月份mon = '二月份' and
a.部门dep = d.部门dep and d.月份mon = '三月份' and
2)
select a.dep,
sum(case when b.mon=1 then b.yj else 0 end) as '一月份',
sum(case when b.mon=2 then b.yj else 0 end) as '二月份',
sum(case when b.mon=3 then b.yj else 0 end) as '三月份',
sum(case when b.mon=4 then b.yj else 0 end) as '四月份',
sum(case when b.mon=5 then b.yj else 0 end) as '五月份',
sum(case when b.mon=6 then b.yj else 0 end) as '六月份',
sum(case when b.mon=7 then b.yj else 0 end) as '七月份',
sum(case when b.mon=8 then b.yj else 0 end) as '八月份',
sum(case when b.mon=9 then b.yj else 0 end) as '九月份',
sum(case when b.mon=10 then b.yj else 0 end) as '十月份',
sum(case when b.mon=11 then b.yj else 0 end) as '十一月份',
sum(case when b.mon=12 then b.yj else 0 end) as '十二月份',
from table2 a left join table1 b on a.dep=b.dep
[6].华为一道面试题
一个表中的Id有多个记录,把所有这个id的记录查出来,并显示共有多少条记录数。
------------------------------------------
select id, Count(*) from tb group by id having count(*)>1
select * from(select count(ID) as count from table group by ID)T where T.count>1
[7]
1.删除表的重复记录
如果记录完全相同才算重复记录,那么: (sql server2000下测试通过)
select distinct * into #tmpp from tid
delete from tid
insert into tid select * from #tmpp
drop table #tmpp
如果有id主键(数字,自增1的那种),那么:(sql server2000下测试通过)
delete from tableA where id not in
(select id = min(id) from tableA group by name)
------------------------------------------
若有两个表如下:
mysql> select * from classes;
+----+--------+
| id | name |
+----+--------+
| 1 | access |
| 2 | j2ee |
+----+--------+
mysql> select * from student;
+-----+---------+-----------+
| sid | classid | sname |
+-----+---------+-----------+
| 1 | 1 | jason |
| 2 | 1 | hwj |
| 3 | 2 | lucene |
| 4 | 2 | hibernate |
+-----+---------+-----------+
要求:若stduent表中的classid与classes表中的id相同的话,则将classes的name值赋给student表中的sname.
如下:
update student s,classes c set s.sname=c.name where s.classid=c.id;
[8]
--主要是熟悉回各计算方法的使用:
表scores有四个字段,学生stu、班级class、学院institute、分数score,
要求返回:班级考试人数大于10、班级最低分在50分以上、计算机学院、班级平均分从高到低前10名。
select class from scores where institute="计算机学院" group by class having count(*)>10 and min(score)>50 order by avg(score) limit 10.
having与where的区别??
HAVING子句可以让我们筛选成组后的各组数据,WHERE子句在聚合前先筛选记录.也就是说作用在GROUP BY 子
句和HAVING子句前;而 HAVING子句在聚合后对组记录进行筛选。
[9]
--对同一表,进行多次查询:
有一个表EMP表: ID , NAME , BRITHDAY
问如何查出同一天生日的雇员?
select a.ID , a.NAME , a.BRITHDAY from EMP a,EMP b where a.BRITHDAY = b.BRITHDAY AND A.ID <>B.ID ;
(9)
反向思维:
-----------------表 student---------------
name object socre
张三 数学 87
张三 语文 73
李四 数学 66
李四 语文 80
王五 数学 88
王五 语文 68
王五 英语 90
怎么查询出所用科目都>80分学生的姓名(SQL)
select * from student where name not in (select name from student where sorce<80 group by name)
思想:先查出存在科目小于80的所有学生,那么全都大于等于80的学生一定不在这些人里面