2013.10.13 事务特性、MYSQL 触发器、存储过程、函数

来源:互联网 发布:潍坊云网络招聘 编辑:程序博客网 时间:2024/05/17 20:24
事务特性:ACID - 原子性、隔离性、一致性、持久性
原子性:不论内部情况如何,从外部看,事务是一个整体--像原子那样不能分割。根据业务逻辑的复杂程度,事务可以简单到一两行SQL语句,也可以复杂到包含上千行的SQL语句。 
一致性:事务能保证数据库的一致性。事务将数据库从一个一致的状态转变为另一个一致的状态。 
隔离性:事务往往是存在并发的。为了简化系统编写的难度,数据库系统必须保证事务之间是相互隔离的,即在一个事务执行时,开发人员不需要关心是否并发。 

持久性:一个事务执行完成以后,数据库系统必须保证事务对数据库的影响已经永久保存下来了。

触发器:可以监视增删改操作、并且可以触发增删改操作。

实例1.

drop TRIGGER trigg;
create trigger trigg before INSERT on test_user for each row //当往test_user表中执行插入语句时,会触发这个触发器
begin

SET @x = "hello trigger";
SELECT @x;
SET New.address="四川.成都";
end;

实例2.

模拟实现购物:增加订单,商品数量减少;删除订单,商品数量增加;
SELECT * from test_buy;
DELETE from test_buy where id=1;// 删除订单,商品数量减少
SELECT * from test_goods;
INSERT INTO test_buy(gid,gcount) values(1,6);//增加订单,商品数量减少
SELECT * from test_goods;
drop TRIGGER addGoodsTrigg;
create trigger addGoodsTrigg AFTER DELETE on test_buy for each row // 删除订单,商品数量增加
     begin
UPDATE test_goods SET count = count+old.gcount where id = old.gid;// 对删除而言,old表示删除的数据
     end;

drop TRIGGER deleteGoodsTrigg;
create trigger deleteGoodsTrigg AFTER INSERT on test_buy for each row // 增加订单,商品数量减少
     begin
UPDATE test_goods SET count = count-New.gcount where id = New.gid;// 对新增而言,new表示新增的数据
     end;

存储过程:http://www.cnblogs.com/kkcheng/archive/2010/03/19/1689672.html
mysql存储过程的优点,存储过程是先编译然后保存在数据库,存储过程就是一些sql的集合【运行速度快】
我最喜欢的是项目发布后,可以在数据库直接修改,而无需修改底层重新编译。【修改方便】
第一:存储过程因为SQL语句已经预编绎过了,因此运行的速度比较快。   
第二:存储过程可以接受参数、输出参数、返回单个或多个结果集以及返回值。可以向程序返回错误原因。     
第三:存储过程运行比较稳定,不会有太多的错误。只要一次成功,以后都会按这个程序运行。     
第四:存储过程主要是在服务器上运行,减少对客户机的压力。   
自定义变量:

(先申明)DECLARE   a INT ; 

(再赋值)SET a=100;    

可用以下语句代替:DECLARE a INT DEFAULT 100;
注:区块定义,常用
begin
......
end;
也可以给区块起别名,如:
lable:begin
...........
end lable;
可以用leave lable;跳出区块,执行区块以后的代码
begin和end如同C语言中的{ 和 }。
条件语句:(都会有结束符)
IF xx THEN XX ELSE XX END IF;
CASE xx WHEN X THEN XX;
WHEN X THEN XX;
ELSE XX;
END CASE;
while循环:
WHILE XX DO XXX END WHILE;
loop循环:
loop_label:LOOP
XXX
IF XX THEN LEAVE loop_label;
END IF;
END LOOP;


实例3.java中使用存储过程:java代码:
sql = "{CALL queryuser()}";
try {
CallableStatementcs = conn.prepareCall(sql);
boolean results = cs.execute();
int i = 0;
while(results) {
System.out.println("result No:----"+(++i));  
         ResultSet rs = cs.getResultSet();  //获取结果集
         while (rs != null && rs.next()) {  
            int id1 = rs.getInt("id");  
            String name1 = rs.getString("name");  
            System.out.println(id1 + ":" + name1);  
         }  
         results = cs.getMoreResults(); //检查是否存在更多结果集  
}
} catch (SQLException e) {
e.printStackTrace();
}

创建过程:
CREATE PROCEDURE queryuser()
BEGIN
SELECT * FROM USER;
END

SQL执行计划:
mysql 如何查看SQL执行计划:explain
SQL索引:CREATE INDEX myindex on persons(lastname);//使用索引

SQL语句的执行原理分析,想提高执行效率的朋友可以参考下。
解析->SQL计划缓存中查找数据->若没有,则查询物理文件,同时将查询结果复制一份到计划缓存中->返回
原理:
第一步:应用程序把查询SQL语句发给服务器端执行。
我们在数据层执行SQL语句时,应用程序会连接到相应的数据库服务器,把SQL语句发送给服务器处理。
第二步:服务器解析请求的SQL语句。
1:SQL计划缓存,经常用查询分析器的朋友大概都知道这样一个事实,往往一个查询语句在第一次运行的时候需要执行特别长的时间,但是如果你马上或者在一定时间内运行同样的语句,会在很短的时间内返回查询结果。  
原因:
1):服务器在接收到查询请求后,并不会马上去数据库查询,而是在数据库中的计划缓存中找是否有相对应的执行计划,如果存在,就直接调用已经编译好的执行计划,节省了执行计划的编译时间。
2):如果所查询的行已经存在于数据缓冲存储区中,就不用查询物理文件了,而是从缓存中取数据,这样从内存中取数据就会比从硬盘上读取数据快很多,提高了查询效率.数据缓冲存储区会在后面提到。
2:如果在SQL计划缓存中没有对应的执行计划,服务器首先会对用户请求的SQL语句进行语法效验,如果有语法错误,服务器会结束查询操作,并用返回相应的错误信息给调用它的应用程序。
注意:此时返回的错误信息中,只会包含基本的语法错误信息,例如select 写成selec等,错误信息中如果包含一列表中本没有的列,此时服务器是不会检查出来的,因为只是语法验证,语义是否正确放在下一步进行。
3:语法符合后,就开始验证它的语义是否正确,例如,表名,列名,存储过程等等数据库对象是否真正存在,如果发现有不存在的,就会报错给应用程序,同时结束查询。
4:接下来就是获得对象的解析锁,我们在查询一个表时,首先服务器会对这个对象加锁,这是为了保证数据的统一性,如果不加锁,此时有数据插入,但因为没有加锁的原因,查询已经将这条记录读入,而有的插入会因为事务的失败会回滚,就会形成脏读的现象。
5:接下来就是对数据库用户权限的验证,SQL语句语法,语义都正确,此时并不一定能够得到查询结果,如果数据库用户没有相应的访问权限,服务器会报出权限不足的错误给应用程序,在稍大的项目中,往往一个项目里面会包含好几个数据库连接串,这些数据库用户具有不同的权限,有的是只读权限,有的是只写权限,有的是可读可写,根据不同的操作选取不同的用户来执行,稍微不注意,无论你的SQL语句写的多么完善,完美无缺都没用。
6:解析的最后一步,就是确定最终的执行计划。当语法,语义,权限都验证后,服务器并不会马上给你返回结果,而是会针对你的SQL进行优化,选择不同的查询算法以最高效的形式返回给应用程序。例如在做表联合查询时,服务器会根据开销成本来最终决定采用hash join,merge join ,还是loop join,采用哪一个索引会更高效等等,不过它的自动化优化是有限的,要想写出高效的查询SQL还是要优化自己的SQL查询语句。
当确定好执行计划后,就会把这个执行计划保存到SQL计划缓存中,下次在有相同的执行请求时,就直接从计划缓存中取,避免重新编译执行计划。
第三步:语句执行。
服务器对SQL语句解析完成后,服务器才会知道这条语句到底表态了什么意思,接下来才会真正的执行SQL语句。
此时分两种情况:
1):如果查询语句所包含的数据行已经读取到数据缓冲存储区的话,服务器会直接从数据缓冲存储区中读取数据返回给应用程序,避免了从物理文件中读取,提高查询速度。
2):如果数据行没有在数据缓冲存储区中,则会从物理文件中读取记录返回给应用程序,同时把数据行写入数据缓冲存储区中,供下次使用。
说明:SQL缓存分好几种,这里有兴趣的朋友可以去搜索一下,有时因为缓存的存在,使得我们很难马上看出优化的结果,因为第二次执行因为有缓存的存在,会特别快速,所以一般都是先消除缓存,然后比较优化前后的性能表现,这里有几个常用的方法:
 DBCC DROPCLEANBUFFERS
 从缓冲池中删除所有清除缓冲区。
 DBCC FREEPROCCACHE
 从过程缓存中删除所有元素。
 DBCC FREESYSTEMCACHE
从所有缓存中释放所有未使用的缓存条目。SQL Server 2005 数据库引擎会事先在后台清理未使用的缓存条目,以使内存可用于当前条目。但是,可以使用此命令从所有缓存中手动删除未使用的条目。
这只能基本消除SQL缓存的影响,目前好像没有完全消除缓存的方案,如果大家有,请指教。
执行顺序:
1. FROM 子句返回初始结果集。
2. WHERE 子句排除不满足搜索条件的行。
3. GROUP BY 子句将选定的行收集到 GROUP BY 子句中各个唯一值的组中。
4. 选择列表中指定的聚合函数可以计算各组的汇总值。
5. 此外,HAVING 子句排除不满足搜索条件的行。
6. 计算所有的表达式;
7. 使用order by对结果集进行排序。
8.查找你要搜索的字段。

详细出处参考:http://www.jb51.net/article/29331.htm

存储过程与函数的区别:
存储过程需要单独执行;  
  函数可以随处调用。  
过程与函数的区别:
过程是指执行某种操作;
函数是指执行某种计算。

MYSQL:系统函数
SELECT 
CASE sex
WHEN 1 THEN '男'
WHEN 0 THEN '女'
ELSE '不确定'
END
FROM carsystem.user
删除表的一列:alter table TEST_USER drop column AGE
只更新当天登陆的民警的退出时间
Update policemenlogin set quitdate=now() WHERE policemenId='p4' and to_days(logindate)=to_days(now())
查过去24小时之内的数据:checkDate >DATE_ADD(now(), INTERVAL -1 DAY)
String sql = "select * from records where vehicleId='" + vehicleId
+ "' and checkDate >DATE_ADD(now(), INTERVAL -1 DAY)"
+ " order by checkDate DESC";
now()系统函数,指当前时间;
DATE_ADD(now(),INTERVAL -1 DAY);系统函数,指当前时间减去1天,即过去24小时时间段内
TO_DAYS(date)/返回一个天数 (从年份0开始的天数) 类似于:SELECT DATEDIFF(now(),'20120112')/指定时间间的天数
SELECT COUNT(*),records.checkdate FROM records GROUP BY TO_DAYS(records.checkdate);
MONTH(date)/返回日期的月份,类似的还有/YEAR/DAY
SELECT COUNT(*),records.checkdate FROM records GROUP BY MONTH(records.checkdate);
DATE_FORMAT(date,'%Y-%m-%d') /按指定格式转换
CURDATE()/返回当前日期
select * from policemenlogin where DATE_FORMAT(logindate,'%Y-%m-%d')=CURDATE();//取当天数据
DATE_SUB(date,INTERVAL NUM UNIT) /从给定时间上减去多少时间
NOW()/系统当前时间
SELECT * FROM policemenlogin WHERE logindate>DATE_SUB(NOW(),INTERVAL 1 DAY);
/按月份分组,
PERIOD_DIFF(date,'200001')//yyyyMM 这里返回2000年01月到checkdate的月份数
EXTRACT(YEAR_MONTH FROM(records.checkdate))mysql时间转换字符串yyyyMM格式
GROUP BY PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM(records.checkdate)),'200001')
GROUP BY PERIOD_DIFF(DATE_FORMAT(records.checkdate,'%Y%m'),'200001')

原创粉丝点击