本文基于oracle
select to_char(sysdate,'YYYY-MM-DD HH24:MI:SS') from dual;
select to_char(1234.56,'$999.99') from dual;
select to_number('$1,000','$9999') from dual;
select count(nvl(comm, 0)) from emp;
--mysql中
select count(ifnull(comm, 0)) from emp;
select deptno, wx_concat(name) from emp grounp by deptno;
--mysql中
select deptno, grounp_concat(name) from emp group by deptno;
select deptno, avg(sal) from emp group by deptno having avg(sal)>3000 order by avg(sal) ;
select deptno, job, sum(sal) from emp group by rollup(deptno, job);
--mysql中
select deptno, job, sum(sal) from emp group by deptno, job with rollup;
上面语句的查询结果相当于下面语句结果相加
select deptno, job, sum(sal) from emp group by deptno, job;
select deptno, sum(sal) from emp group by deptno;
select sum(sal) from emp;
笛卡尔积是多表查询的基础
select e.deptno, e.ename, e.sal, d.dname
from emp e, dept d where e.deptno = d.deptno;
select e.deptno, e.ename, e.sal, s.grade
from emp e, salgrade s where e.sal between s.lowsal and s.hisal;
select d.deptno, d.dname, count(e.empno) from emp e, dept d where e.deptno = d.deptno group by d.deptno, d.name;
上面的语句是不对的,对于没有人员的部门不满足上面的where,所以这时我们需要使用右外连接将部门表中不满足条件的记录包含到最后的结果当中。
--注意:此处是右连接
select d.deptno, d.dname, count(e.empno) from emp e, dept d where e.deptno(+) = d.deptno group by d.deptno, d.name;
select e.ename, p.ename
from emp e, emp p where e.mgr = p.empno
注意:自连接不适用于大表,因为根据笛卡尔积,会产出庞大的记录,这时我们需要使用层次查询。层次查询还是在一张表中查询,层次查询中会建立一颗树,层次查询可以从书中的任意节点开始往下查找
--level是伪列,表示节点在树中的层级,根节点在第一层
select level, empno, ename, sal, mgr from emp connect by prior empno = mgr start with empno = 7563 order by 1;
-- start with mgr is null order by 1;
select rownum, empno, ename, sal from emp where rownum <=3 order by sal desc;
执行这条查询语句最终得不到正确的结果,原因就是rownum永远按照默认的顺序生成。得到正确结果,需要使用下面的语句:
select rownum, empno, ename, sal from (select * from emp order by sal desc) where rownum <=3;
mysql没有rownum伪列,可以使用下面的方式生成行号
set @rownum = 0;
select empno, ename, sal from (select @rownum:=@rownum+1 as rownum, empno, ename, sal from emp order by sal desc) as temp where temp.rownum <=3 ;
select empno, ename, sal, (select avg(sal) from emp where deptno = e.deptno) avgsal from emp e where sal > (select avg(sal) from emp where deptno = e.deptno);
--单行子查询示例
select * from emp where job = (select job from emp where empno = 7566) andr sal > (select sal from emp where empno = 7568);
--多行子查询
select * from emp where sal any (select sal from emp where empno = 30);
子查询中的空值问题
对于单行子查询,如果返回的结果为空,那么最终的结果也即为空。
对于多行子查询返回结果中包含空值,看下官方的一段说明:
对于多行子查询返回结果中包含空值的情况,我们要在子查询中排除空值的情况。
使用exists代替in,使用no exists代替not in
--效率高
select loginname, pwd, vip_id from tb_vip v where exists (select * from tb_terminal where vip_id = v.vip_id group by vip_id having count(vip_id)>100);
--效率低
select loginname, pwd, vip_id from tb_vip where vip_id in (select vip_id from tb_terminal group by vip_id having count(vip_id) > 100);
PLSQL是在SQL语言中增加过程处理语句,如分支、循环,使SQL语言具有过程处理能力。
PLSQL基本结构:
declare
声明部分(变量、光标、例外)
begin 语句序列(DML语句) exception 例外处理语句 end;
/
--引用型变量
set serveroutput on declare pename emp.ename%type;
psal emp.sal%type;
begin select ename, sal into pename, psal from emp where empno = 7838;
dbms_output.put_line(pename || '的薪水是' || psal);
end;
/
记录型变量保存了整条记录的值
--记录型变量
set serveroutput on declare emp_rec emp%rowtype;
begin select * into emp_rec from emp where empno = 7838;
dbms_output.put_line(emp_rec.ename || '的薪水是' || remp_rec.sal);
end;
/
set serveroutput on --接受一个键盘输入值 --num:地址量 accept num prompt '请输入一个数字' declare pnum number := #--取地址值
begin if pnum = 0 then dbms_output.put_line('输入数字是0');
elsif pnum = 1 then dbms_output.put_line('输入数字是0');
else dbms_output.put_line('其他数字');
end if;
end;
/
while total <= 3000 loop
循环体
end loop;
(2) loop循环
loop
exit when 退出条件
循环体
end loop;
(3) for循环
for i in 1..3 loop
循环语句
end loop;
示例
set serveroutput on declare pnum number := 0;
begin loop exit when pnum > 10;
pnum := pnum+1;
end loop;
end;
/
光标在declare关键字下声明
cursor 光标名[(参数名 参数类型[,参数名 参数类型]…)] is select 语句
打开光标:open 光标名;
取光标中一行值: fetch 广播名 into 变量名;
关闭关闭:close 光标名;
set serveroutput on declare cursor c1 is select ename, sal from emp;
pename emp.ename%type;
psal emp.sal%type;
begin open c1;
loop
fetch c1 into pename, psal;
exit when c1%notfound;
dbms_output.put_line(pename||'的薪水是'||psal);
end loop;
close c1;
end;
/
set serveroutput on
declare
pename emp.ename%type;
begin
select ename into pename from emp where empno = 1234;
exception
when no_data_found then dbms_output.put_line('没有找到该员工');
when others then dbms_output.put_line('其它例外');
end;
/
set serveroutput on declare cursor cemp is select ename from emp where deptno = 30;
pename emp.ename%type;
--自定义异常
no_emp_found exception;
begin open cemp;
fetch cemp into pename;
if cemp%notfound then raise no_emp_found;
end if;
close cemp;
exception
when no_emp_found then insert into emp values('fetch语句没有获得数据或者数据已经处理完');
end;
/
使用下面的语法来创建存储过程:
create [or repalce] procedure 过程名(参数列表)
as 声明变量信息(不可省略)
plsql程序体
create or replace procedure queryinfo(eno in number, pename out varchar2, psal out number, pjob out varchar) as begin select ename, sal, empjob into pename, psal, pjob from emp where empno = eno;
end;
/
函数为一命名的存储程序,可带参数,并返回一个计算结果。函数和过程的结果类似,但必须有一个return子句,用于返回函数值。使用下面的语法创建存储函数:
create [or repalce] function 过程名(参数列表) return 返回值类型
as 声明变量信息(不可省略)
plsql程序体
查询员工年收入
create or replace function(eno in number) return number as psal emp.sal%type;
pcomm emp.comm%type;
begin select sal, comm into psal, pcomm from emp where empno = eno;
return psal*12+nvl(pcomm, 0);
end;
/
程序包包含包头和包体,包使得out参数可以返回多行记录。
create or replace package mypackage as
type empcursor is ref cursor;
procedure queryEmpList(dno in number, empList out empcursor) end mypackage;
create or replace package body mypackage as procedure queryEmpList(dno in number, empList out empcursor) as begin open empList for select * from emp where deptno = dno;
end queryEmpList;
end mypackage;
public void testCursor()
{
String sql = "{call MYPACKAGE.queryEmpList(?,?)}";
Connection conn = null;
CallableStatement call = null;
ResultSet rs = null;
try
{
conn = JDBCUtils.getConnection();
call = conn.prepareCall(sql);
call.setInt(1,10);
call.registerOutParameter(2, OracleTypes.CURSOR);
call.execute();
rs = (OracleCallableStatement)CallableStatement.getCursor(2);
while(rs.next)
{
int empno = rs.getInt("empno");
String name = rs.getString("ename");
double sal = rs.getDouble("sal");
String job = rs.getString("empjob");
}
}catch(Exception ex)
{
ex.printStackTrack();
}finally
{
JDBCUtils.closeConnection(rs, call, conn);
}
}
数据库触发器是一个与表关联的,存储的PL/SQL程序。每当一个特定的数据操作语句(insert、delete、update)在指定的表上发生时,oracle自动的执行触发器中定义的语句序列。
使用下面的语法创建一个触发器
create [or replace] trigger 触发器名称
{before|after} {insert|delete|update [of 列名]}
on 表名
[for each row [when(条件)]]
plsql程序体
create or replace trigger securityemp before insert on emp begin if to_char(sysdate, 'day') in ('星期六','星期日') or to_number(to_char(sysdate,'hh24')) no between 9 and 18 then raise_appliction_error(-20001,'禁止在非工作时间插入新员工');
end if;
end;
/
create or replace trigger checksalary before update on emp for each row begin if :old.sal > :new.sal then raise_application_error(-20002,'涨后的薪水不能少于涨后的薪水');
end if;
end;
/
create or replace trigger snyc_salary after update on emp for each row begin update emp_back set sal = :new.sal where empno = :new.empno;
end;
/