Java的Mysql数据库的面试题

数据库部分

1、用两种方式根据部门号从高到低,工资从低到高列出每个员工的信息。

employee:  eid,ename,salary,deptid;  
select * from employee order by deptid desc,salary 

2、列出各个部门中工资高于本部门的平均工资的员工数和

部门号,并按部门号排序

创建表:     
mysql> create table employee921(id int primary key auto_increment,name varchar(5 0),salary bigint,deptid int); 

插入实验数据:
 mysql> insert into employee921 values(null,'zs',1000,1),(null,'ls',1100,1),(null ,'ww',1100,1),(null,'zl',900,1) ,(null,'zl',1000,2), (null,'zl',900,2) ,(null,'z l',1000,2) , (null,'zl',1100,2); 

编写 sql 语句:

(1)select avg(salary) from employee921 group by deptid; 
(2)mysql> select employee921.id,employee921.name,employee921.salary,employee921.dep tid tid from  employee921 where salary > (select avg(salary) from employee921 where  deptid = tid);  

效率低的一个语句,仅供学习参考使用(在 group by 之后不能使用 where,只能使用 having,在 group by 之前可以使用 where,即表示对过滤后的结果分组):

mysql> select employee921.id,employee921.name,employee921.salary,employee921.dep tid tid from  employee921 where salary > (select avg(salary) from employee921 group by deptid 
having deptid = tid); 
(3)select count(*) ,tid   from 
(select employee921.id,employee921.name,employee921.salary,employee921.deptid tid   from employee921    where salary >     (select avg(salary) from employee921 where  deptid = tid)  ) as t   group by tid ; 

另外一种方式:关联查询

select a.ename,a.salary,a.deptid   from emp a,     (select deptd,avg(salary) avgsal from emp group by deptid ) b   where a.deptid=b.deptid and a.salary>b.avgsal; 

3、存储过程与触发器必须讲,经常被面试到?

create procedure insert_Student (_name varchar(50),_age int ,out _id int) begin  insert into student value(null,_name,_age);  select max(stuId) into _id from student; end; 
call insert_Student('wfz',23,@id); select @id; 
mysql> create trigger update_Student BEFORE update on student FOR EACH ROW -> select * from student; 触发器不允许返回结果 
create trigger update_Student BEFORE update on student FOR EACH ROW   insert into  student value(null,'zxx',28); mysql 的触发器目前不能对当前表进行操作
create trigger update_Student BEFORE update on student FOR EACH ROW   delete from articles  where id=8; 

这个例子不是很好,最好是用删除一个用户时,顺带删除该用户的所有帖子 这里要注意使用 OLD.id

触发器用处还是很多的,比如校内网、开心网、Facebook,你发一个日志,自动通知好 友,其实就是在增加日志时做一个后触发,再向通知表中写入条目。因为触发器效率高。而 UCH 没有用触发器,效率和数据处理能力都很低。 存储过程的实验步骤:

mysql> delimiter | mysql> create procedure insertArticle_Procedure (pTitle varchar(50),pBid int,out  pId int)     -> begin     -> insert into article1 value(null,pTitle,pBid);     -> select max(id) into pId from article1;     -> end;     -> | Query OK, 0 rows affected (0.05 sec) 
mysql> call insertArticle_Procedure('传智播客',1,@pid);     -> | Query OK, 0 rows affected (0.00 sec) 
mysql> delimiter ; mysql> select @pid; +------+ | @pid | +------+ | 3    | +------+ 1 row in set (0.00 sec) 
mysql> select * from article1; +----+--------------+------+ | id | title        | bid  | +----+--------------+------+ | 1  | test         | 1    | | 2  | chuanzhiboke | 1    | | 3  | 传智播客     | 1    | +----+--------------+------+ 3 rows in set (0.00 sec) 

触发器的实验步骤:

create table board1(id int primary key auto_increment,name varchar(50),ar ticleCount int); 

create table article1(id int primary key auto_increment,title varchar(50) ,bid int references board1(id)); delimiter | 

create trigger insertArticle_Trigger after insert on article1 for each ro w begin

-> update board1 set articleCount=articleCount+1 where id= NEW.bid;     -> end;   delimiter ; 
insert into board1 value (null,'test',0); 

insert into article1 value(null,'test',1); 

还有,每插入一个帖子,都希望将版面表中的最后发帖时间,帖子总数字段进行同步 更新,用触发器做效率就很高。下次课设计这样一个案例,写触发器时,对于最后发帖时间 可能需要用 declare 方式声明一个变量,或者是用 NEW.posttime 来生成。

4、数据库三范式是什么?

第一范式(1NF)
字段具有原子性,不可再分。所有关系型数据库系统都满足第一范式) 数据库表中的字段都是单一属性的,不可再分。例如,姓名字段,其中的姓和名必须作 为一个整体,无法区分哪部分是姓,哪部分是名,如果要区分出姓和名,必须设计成两个独 立的字段。
第二范式(2NF)
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必 须先满足第一范式(1NF)。 要求数据库表中的每个实例或行必须可以被惟一地区分。通常需要为表加上一个列,以存储 各个实例的惟一标识。这个惟一属性列被称为主关键字或主键。
要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖 主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成 一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列, 以存储各个实例的惟一标识。简而言之,第二范式就是非主属性非部分依赖于主关键字。 第三范式的要求如下: 满足第三范式(3NF)必须先满足第二范式(2NF)。
第三范式(3NF):
要求一个 数据库表中不包含已在其它表中已包含的非主关键字信息。 所以第三范式具有如下特征: 1,每一列只有一个值 2,每一行都能区分。 3,每一个表都不包含其他表已经包含的非主关键字信息。 例如,帖子表中只能出现发帖人的 id,而不能出现发帖人的 id,还同时出现发帖人姓名, 否则,只要出现同一发帖人 id 的所有记录,它们中的姓名部分都必须严格保持一致,这就 是数据冗余。

5、说出一些数据库优化方面的经验?

用 PreparedStatement 一般来说比 Statement 性能高:
一个 sql 发给服务器去执行,涉及步骤: 语法检查、语义分析, 编译,缓存
“inert into user values(1,1,1)”-
二进制 “inert into user values(2,2,2)”-
二进制 “inert into user values(?,?,?)”-
有外键约束会影响插入和删除性能,如果程序能够保证数据的完整性,那在设计数据库时就 去掉外键。(比喻:就好比免检产品,就是为了提高效率,充分相信产品的制造商) (对于 hibernate 来说,就应该有一个变化:empleyee->Deptment 对象,现在设计时就成了 employeedeptid)

看 mysql 帮助文档子查询章节的最后部分,例如,根据扫描的原理,下面的子查询语句要比 第二条关联查询的效率高:

  1. select e.name,e.salary where e.managerid=(select id from employee where name=‘zxx’);
  2. select e.name,e.salary,m.name,m.salary from employees e,employees m where e.managerid = m.id and m.name=‘zxx’;

表中允许适当冗余,譬如,主题帖的回复数量和最后回复时间等 将姓名和密码单独从用户表中独立出来。这可以是非常好的一对一的案例哟!

sql 语句全部大写,特别是列名和表名都大写。特别是 sql 命令的缓存功能,更加需要统一 大小写,sql 语句发给 oracle 服务器语法检查和编译成为内部指令缓存和执行指令。 根据缓存的特点,不要拼凑条件,而是用?和 PreparedStatment
还有索引对查询性能的改进也是值得关注的。

备注:下面是关于性能的讨论举例

4 航班 3 个城市
m*n

select * from flight,city where flight.startcityid=city.cityid and city.name='beijing'; 

m + n

select * from flight where startcityid = (select cityid from city where cityname='beijing'); 
select flight.id,'beijing',flight.flightTime from flight where startcityid = (select cityid from city where cityname='beijing') 

6、union 和 union all 有什么不同?

假设我们有一个表 Student,包括以下字段与数据:

drop table student; create table student ( id int primary key, name nvarchar2(50) not null, score number not null ); insert into student values(1,'Aaron',78);
insert into student values(2,'Bill',76);
insert into student values(3,'Cindy',89);
insert into student values(4,'Damon',90); 
insert into student values(5,'Ella',73); insert into student values(6,'Frado',61); 
insert into student values(7,'Gill',99); insert into student values(8,'Hellen',56); 
insert into student values(9,'Ivan',93); insert into student values(10,'Jay',90); commit; 

Union 和 Union All 的区别。
select * from student where id < 4 union select * from student where id > 2 and id < 6
结果将是
1 Aaron 78 2 Bill 76 3 Cindy 89 4 Damon 90 5 Ella 73
如果换成 Union All 连接两个结果集,则返回结果是:
1 Aaron 78 2 Bill 76 3 Cindy 89 3 Cindy 89 4 Damon 90

5 .可以看出,Union 和 Union All 的区别之一在于对重复结果的处理。
UNION 在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进 行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常 见的是过程表与历史表 UNION。
如: select * from gc_dfys union select * from ls_jg_dfys
这个 SQL 在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最 后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。 而 UNION ALL 只是简单的将两个结果合并后就返回。这样,如果返回的两个结果集中有 重复的数据,那么返回的结果集就会包含重复的数据了。
从效率上说,UNION ALL 要比 UNION 快很多,所以,如果可以确认合并的两个结果集 中不包含重复的数据的话,那么就使用 UNION ALL

7.分页语句

取出 sql 表中第 31 到 40 的记录(以自动增长 ID 为主键) sql server 方案

 1:  select top 10 * from t where id not in (select top 30 id from t order by id ) orde by id sql server 
 2:  select top 10 * from t where id in (select top 40 id from t order by id) order by id desc 
mysql 方案:select * from t order by id limit 30,10 

oracle 方案:select * from (select rownum r,* from t where r<=40) where r>30 

--------------------待整理进去的内容------------------------------------- pageSize=20; pageNo = 5;

1.分页技术 1(直接利用 sql 语句进行分页,效率最高和最推荐的)
mysql:sql = "select * from articles limit " + (pageNo-1)*pageSize + "," + pageSize; oracle: sql = "select * from " + "(select rownum r,* from " + "(select * from articles order by postime desc)" + "where rownum<= " + pageNo*pageSize +") tmp " + "where r>" + (pageNo-1)*pageSize;
注释:第 7 行保证 rownum 的顺序是确定的,因为 oracle 的索引会造成 rownum 返回不同的

简洋提示:没有 order by 时,rownum 按顺序输出,一旦有了 order by,rownum 不按顺序输 出了,这说明 rownum 是排序前的编号。如果对 order by 从句中的字段建立了索引,那么, rownum 也是按顺序输出的,因为这时候生成原始的查询结果集时会参照索引表的顺序来构 建。

sqlserver:sql = "select top 10 * from id not id(select top " + (pageNo-1)*pageSize + "id from articles)" 
DataSource ds = new InitialContext().lookup(jndiurl); Connection cn = ds.getConnection(); //"select * from user where id=?"  --->binary directive PreparedStatement pstmt = cn.prepareSatement(sql); ResultSet rs = pstmt.executeQuery() while(rs.next()) {  out.println(rs.getString(1)); } 

2.不可滚动的游标 pageSize=20; pageNo = 5; cn = null stmt = null; rs = null; try { sqlserver:sql = “select * from articles”;

DataSource ds = new InitialContext().lookup(jndiurl); Connection cn = ds.getConnection(); //“select * from user where id=?” —>binary directive PreparedStatement pstmt = cn.prepareSatement(sql); ResultSet rs = pstmt.executeQuery() for(int j=0;j<(pageNo-1)*pageSize;j++) { rs.next(); }

int i=0;

while(rs.next() && i<10) { i++; out.println(rs.getString(1));

} } cacth(){} finnaly { if(rs!=null)try{rs.close();}catch(Exception e){} if(stm… if(cn… }

3.可滚动的游标 pageSize=20; pageNo = 5; cn = null stmt = null; rs = null; try { sqlserver:sql = “select * from articles”;

DataSource ds = new InitialContext().lookup(jndiurl); Connection cn = ds.getConnection(); //“select * from user where id=?” —>binary directive PreparedStatement pstmt = cn.prepareSatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,…); //根据上面这行代码的异常 SQLFeatureNotSupportedException,就可判断驱动是否支持可滚 动游标

ResultSet rs = pstmt.executeQuery() rs.absolute((pageNo-1)*pageSize) int i=0; while(rs.next() && i<10) { i++; out.println(rs.getString(1)); } } cacth(){} finnaly { if(rs!=null)try{rs.close();}catch(Exception e){} if(stm… if(cn… }

美河学习在线 www.eimhe.com

8.用一条 SQL 语句 查询出每门课都大于 80 分的学生姓名

name kecheng fenshu 张三 语文 81 张三 数学 75 李四 语文 76 李四 数学 90 王五 语文 81 王五 数学 100 王五 英语 90 、

准备数据的 sql 代码: create table score(id int primary key auto_increment,name varchar(20),subject varchar(20),score int); insert into score values (null,‘张三’,‘语文’,81), (null,‘张三’,‘数学’,75), (null,‘李四’,‘语文’,76), (null,‘李四’,‘数学’,90), (null,‘王五’,‘语文’,81), (null,‘王五’,‘数学’,100), (null,'王五 ',‘英语’,90);

提示:当百思不得其解时,请理想思维,把小变成大做,把大变成小做,

答案: A: select distinct name from score where name not in (select distinct name from score where score<=80)

B:select distince name t1 from score where 80< all (select score from score where name=t1);

9.所有部门之间的比赛组合

一个叫 department 的表,里面只有一个字段 name,一共有 4 条纪录,分别是 a,b,c,d,对应 四个球对,现在四个球对进行比赛,用一条 sql 语句显示所有可能的比赛组合.

答:select a.name, b.name from team a, team b where a.name < b.name

10.每个月份的发生额都比 101 科目多的科目

请用 SQL 语句实现:从 TestDB 数据表中查询出所有月份的发生额都比 101 科目相应月份的 发生额高的科目。请注意:TestDB 中有很多科目,都有 1-12 月份的发生额。 AccID:科目代码,Occmonth:发生额月份,DebitOccur:发生额。 数据库名:JcyAudit,数据集:Select * from TestDB 准备数据的 sql 代码: drop table if exists TestDB; create table TestDB(id int primary key auto_increment,AccID varchar(20), Occmonth date, DebitOccur bigint); insert into TestDB values (null,‘101’,‘1988-1-1’,100), (null,‘101’,‘1988-2-1’,110), (null,‘101’,‘1988-3-1’,120), (null,‘101’,‘1988-4-1’,100), (null,‘101’,‘1988-5-1’,100), (null,‘101’,‘1988-6-1’,100), (null,‘101’,‘1988-7-1’,100), (null,‘101’,‘1988-8-1’,100); --复制上面的数据,故意把第一个月份的发生额数字改小一点 insert into TestDB values (null,‘102’,‘1988-1-1’,90), (null,‘102’,‘1988-2-1’,110), (null,‘102’,‘1988-3-1’,120), (null,‘102’,‘1988-4-1’,100), (null,‘102’,‘1988-5-1’,100), (null,‘102’,‘1988-6-1’,100), (null,‘102’,‘1988-7-1’,100), (null,‘102’,‘1988-8-1’,100); --复制最上面的数据,故意把所有发生额数字改大一点 insert into TestDB values (null,‘103’,‘1988-1-1’,150), (null,‘103’,‘1988-2-1’,160), (null,‘103’,‘1988-3-1’,180), (null,‘103’,‘1988-4-1’,120), (null,‘103’,‘1988-5-1’,120), (null,‘103’,‘1988-6-1’,120), (null,‘103’,‘1988-7-1’,120), (null,‘103’,‘1988-8-1’,120); --复制最上面的数据,故意把所有发生额数字改大一点 insert into TestDB values (null,‘104’,‘1988-1-1’,130), (null,‘104’,‘1988-2-1’,130), (null,‘104’,‘1988-3-1’,140),

(null,‘104’,‘1988-4-1’,150), (null,‘104’,‘1988-5-1’,160), (null,‘104’,‘1988-6-1’,170), (null,‘104’,‘1988-7-1’,180), (null,‘104’,‘1988-8-1’,140); --复制最上面的数据,故意把第二个月份的发生额数字改小一点 insert into TestDB values (null,‘105’,‘1988-1-1’,100), (null,‘105’,‘1988-2-1’,80), (null,‘105’,‘1988-3-1’,120), (null,‘105’,‘1988-4-1’,100), (null,‘105’,‘1988-5-1’,100), (null,‘105’,‘1988-6-1’,100), (null,‘105’,‘1988-7-1’,100), (null,‘105’,‘1988-8-1’,100); 答案: select distinct AccID from TestDB where AccID not in (select TestDB.AccIDfrom TestDB, (select * from TestDB where AccID=‘101’) as db101 where TestDB.Occmonth=db101.Occmonth and TestDB.DebitOccur<=db101.DebitOccur );

11.统计每年每月的信息

year month amount 1991 1 1.1 1991 2 1.2 1991 3 1.3 1991 4 1.4 1992 1 2.1 1992 2 2.2 1992 3 2.3 1992 4 2.4 查成这样一个结果 year m1 m2 m3 m4 1991 1.1 1.2 1.3 1.4 1992 2.1 2.2 2.3 2.4

提示:这个与工资条非常类似,与学生的科目成绩也很相似。
准备 sql 语句: drop table if exists sales;

create table sales(id int auto_increment primary key,year varchar(10), month varchar(10), amount float(2,1)); insert into sales values (null,‘1991’,‘1’,1.1), (null,‘1991’,‘2’,1.2), (null,‘1991’,‘3’,1.3), (null,‘1991’,‘4’,1.4), (null,‘1992’,‘1’,2.1), (null,‘1992’,‘2’,2.2), (null,‘1992’,‘3’,2.3), (null,‘1992’,‘4’,2.4);

答案一、 select sales.year , (select t.amount from sales t where t.month=‘1’ and t.year= sales.year) ‘1’, (select t.amount from sales t where t.month=‘1’ and t.year= sales.year) ‘2’, (select t.amount from sales t where t.month=‘1’ and t.year= sales.year) ‘3’, (select t.amount from sales t where t.month=‘1’ and t.year= sales.year) as ‘4’ from sales group by year;

12.显示文章标题,发帖人、最后回复时间

表:id,title,postuser,postdate,parentid 准备 sql 语句: drop table if exists articles; create table articles(id int auto_increment primary key,title varchar(50), postuser varchar(10), postdate datetime,parentid int references articles(id)); insert into articles values (null,‘第一条’,‘张三’,‘1998-10-10 12:32:32’,null), (null,‘第二条’,‘张三’,‘1998-10-10 12:34:32’,null), (null,‘第一条回复 1’,‘李四’,‘1998-10-10 12:35:32’,1), (null,‘第二条回复 1’,‘李四’,‘1998-10-10 12:36:32’,2), (null,‘第一条回复 2’,‘王五’,‘1998-10-10 12:37:32’,1), (null,‘第一条回复 3’,‘李四’,‘1998-10-10 12:38:32’,1), (null,‘第二条回复 2’,‘李四’,‘1998-10-10 12:39:32’,2), (null,‘第一条回复 4’,‘王五’,‘1998-10-10 12:39:40’,1);

答案: select a.title,a.postuser, (select max(postdate) from articles where parentid=a.id) reply from articles a where a.parentid is null;

注释:子查询可以用在选择列中,也可用于 where 的比较条件中,还可以用于 from 从句中。

13.删除除了 id 号不同,其他都相同的学生冗余信息

2.学生表 如下: id 号 学号 姓名 课程编号 课程名称 分数 1 2005001 张三 0001 数学 69 2 2005002 李四 0001 数学 89 3 2005001 张三 0001 数学 69 A: delete from tablename where id 号 not in(select min(id 号) from tablename group by 学号,姓 名,课程编号,课程名称,分数) 实验: create table student2(id int auto_increment primary key,code varchar(20),name varchar(20)); insert into student2 values(null,‘2005001’,‘张三’),(null,‘2005002’,‘李四’),(null,‘2005001’,‘张三’);

//如下语句,mysql 报告错误,可能删除依赖后面统计语句,而删除又导致统计语句结果不 一致。

delete from student2 where id not in(select min(id) from student2 group by name); //但是,如下语句没有问题: select * from student2 where id not in(select min(id) from student2 group by name); //于是,我想先把分组的结果做成虚表,然后从虚表中选出结果,最后再将结果作为删除的 条件数据。 delete from student2 where id not in(select mid from (select min(id) mid from student2 group by name) as t); 或者: delete from student2 where id not in(select min(id) from (select * from s tudent2) as t group by t.name);

14.航空网的几个航班查询题:

表结构如下: flight{flightID,StartCityID ,endCityID,StartTime} city{cityID, CityName) 实验环境: create table city(cityID int auto_increment primary key,cityName varchar(20)); create table flight (flightID int auto_increment primary key, StartCityID int references city(cityID), endCityID int references city(cityID), StartTime timestamp); //航班本来应该没有日期部分才好,但是下面的题目当中涉及到了日期 insert into city values(null,‘北京’),(null,‘上海’),(null,‘广州’); insert into flight values (null,1,2,‘9:37:23’),(null,1,3,‘9:37:23’),(null,1,2,‘10:37:23’),(null,2,3,‘10:37:23’);

1、查询起飞城市是北京的所有航班,按到达城市的名字排序

参与运算的列是我起码能够显示出来的那些列,但最终我不一定把它们显示出来。各个表组 合出来的中间结果字段中必须包含所有运算的字段。

select * from flight f,city c where f.endcityid = c.cityid and startcityid = (select c1.cityid from city c1 where c1.cityname = “北京”) order by c.cityname asc;

mysql> select flight.flightid,‘北京’ startcity, e.cityname from flight,city e wh ere flight.endcityid=e.cityid and flight.startcityid=(select cityid from city wh ere cityname=‘北京’);

mysql> select flight.flightid,s.cityname,e.cityname from flight,city s,city e wh ere flight.startcityid=s.cityid and s.cityname=‘北京’ and flight.endCityId=e.cit yID order by e.cityName desc;

2、查询北京到上海的所有航班纪录(起飞城市,到达城市,起飞时间,航班号) select c1.CityName,c2.CityName,f.StartTime,f.flightID from city c1,city c2,flight f where f.StartCityID=c1.cityID and f.endCityID=c2.cityID and c1.cityName=‘北京’ and c2.cityName=‘上海’ 3、查询具体某一天(2005-5-8)的北京到上海的的航班次数 select count(*) from (select c1.CityName,c2.CityName,f.StartTime,f.flightID from city c1,city c2,flight f where f.StartCityID=c1.cityID and f.endCityID=c2.cityID and c1.cityName=‘北京’ and c2.cityName=‘上海’ and 查帮助获得的某个日期处理函数(startTime) like ‘2005-5-8%’

mysql 中提取日期部分进行比较的示例代码如下: select * from flight where date_format(starttime,’%Y-%m-%d’)=‘1998-01-02’

15.查出比经理薪水还高的员工信息:

Drop table if not exists employees;

美河学习在线 www.eimhe.com

create table employees(id int primary key auto_increment,name varchar(50) ,salary int,managerid int references employees(id)); insert into employees values (null,’ lhm’,10000,null), (null,’ zxx’,15000,1 ),(null,‘flx’,9000,1),(null,‘tg’,10000,2),(null,‘wzg’,10000,3);

Wzg 大于 flx,lhm 大于 zxx

解题思路: 根据 sql 语句的查询特点,是逐行进行运算,不可能两行同时参与运算。 涉及了员工薪水和经理薪水,所有,一行记录要同时包含两个薪水,所有想到要把这个表自 关联组合一下。 首先要组合出一个包含有各个员工及该员工的经理信息的长记录,譬如,左半部分是 员工,右半部分是经理。而迪卡尔积会组合出很多垃圾信息,先去除这些垃圾信息。

select e.* from employees e,employees m where e.managerid=m.id and e.sala ry>m.salary;

16、求出小于 45 岁的各个老师所带的大于 12 岁的学生人数

数据库中有 3 个表 teacher 表,student 表,tea_stu 关系表。 teacher 表 teaID name age student 表 stuID name age teacher_student 表 teaID stuID 要求用一条 sql 查询出这样的结果 1.显示的字段要有老师 name, age 每个老师所带的学生人数 2 只列出老师 age 为 40 以下,学生 age 为 12 以上的记录 预备知识: 1.sql 语句是对每一条记录依次处理,条件为真则执行动作(select,insert,delete,update) 2.只要是迪卡尔积,就会产生“垃圾”信息,所以,只要迪卡尔积了,我们首先就要想到清除“垃圾”信息 实验准备: drop table if exists tea_stu; drop table if exists teacher; drop table if exists student; create table teacher(teaID int primary key,name varchar(50),age int); create table student(stuID int primary key,name varchar(50),age int); create table tea_stu(teaID int references teacher(teaID),stuID int references student(stuID)); insert into teacher values(1,‘zxx’,45), (2,‘lhm’,25) , (3,‘wzg’,26) , (4,‘tg’,27); insert into student values(1,‘wy’,11), (2,‘dh’,25) , (3,‘ysq’,26) , (4,‘mxc’,27); insert into tea_stu values(1,1), (1,2), (1,3); insert into tea_stu values(2,2), (2,3), (2,4); insert into tea_stu values(3,3), (3,4), (3,1); insert into tea_stu values(4,4), (4,1), (4,2) , (4,3);

结果:23,32,43

解题思路:(真实面试答题时,也要写出每个分析步骤,如果纸张不够,就找别人要) 1 要会统计分组信息,统计信息放在中间表中: select teaid,count(*) from tea_stu group by teaid;

2 接着其实应该是筛除掉小于 12 岁的学生,然后再进行统计,中间表必须与 student 关联才能得到 12 岁以下学 生和把该学生记录从中间表中剔除,代码是: select tea_stu.teaid,count(*) total from student,tea_stu where student.stuid=tea_stu.stuid and student.age>12 group by tea_stu.teaid

3.接着把上面的结果做成虚表与 teacher 进行关联,并筛除大于 45 的老师 select teacher.teaid,teacher.name,total from teacher ,(select tea_stu.tea id,count(*) total from student,tea_stu where student.stuid=tea_stu.stuid and stu dent.age>12 group by tea_stu.teaid) as tea_stu2 where teacher.teaid=tea_stu2.tea id and teacher.age<45;

17.求出发帖最多的人:

select authorid,count() total from articles group by authorid having total= (select max(total2) from (select count() total2 from articles group by authorid) as t);

select t.authorid,max(t.total) from (select authorid,count(*) total from articles )as t 这条语句不行,因为 max 只有一列,不能与其他列混淆。

select authorid,count(*) total from articles group by authorid having total=max(total)也不行。

18、一个用户表中有一个积分字段,假如数据库中有 100 多万个用户,若要在每年第一天凌晨将积分清零,你将考虑什么,你将想什么办法解决?

alter table drop column score; alter table add colunm score int; 可能会很快,但是需要试验,试验不能拿真实的环境来操刀,并且要注意, 这样的操作时无法回滚的,在我的印象中,只有 inert update delete 等 DML 语句才能回滚,

对于 create table,drop table ,alter table 等 DDL 语句是不能回滚。

解决方案一,update user set score=0; 解决方案二,假设上面的代码要执行好长时间,超出我们的容忍范围,那我就 alter table user drop column score;alter table user add column score int。

下面代码实现每年的那个凌晨时刻进行清零。 Runnable runnable = new Runnable(){ public void run(){ clearDb(); schedule(this,new Date(new Date().getYear()+1,0,0)); } };

schedule(runnable, new Date(new Date().getYear()+1,0,1));

19、一个用户具有多个角色,请查询出该表中具有该用户的所有角色的其他用户。

select count(*) as num,tb.id from tb, (select role from tb where id=xxx) as t1 where tb.role = t1.role and tb.id != t1.id group by tb.id having num = select count(role) from tb where id=xxx;

20. xxx 公司的 sql 面试

Table EMPLOYEES Structure: EMPLOYEE_ID NUMBER Primary Key, FIRST_NAME VARCHAR2(25), LAST_NAME VARCHAR2(25), Salary number(8,2), HiredDate DATE, Departmentid number(2) Table Departments Structure: Departmentid number(2) Primary Key,

美河学习在线 www.eimhe.com

DepartmentName VARCHAR2(25).

(2)基于上述 EMPLOYEES 表写出查询:写出雇用日期在今年的,或者工资在[1000,2000] 之间的,或者员工姓名(last_name)以’Obama’打头的所有员工,列出这些员工的全部个人 信息。 (4 分) select * from employees where Year(hiredDate) = Year(date()) or (salary between 1000 and 200) or left(last_name,3)=‘abc’;

(3) 基于上述 EMPLOYEES 表写出查询:查出部门平均工资大于 1800 元的部门的所有员工, 列出这些员工的全部个人信息。 (4 分) mysql> select id,name,salary,deptid did from employee1 where (select avg(salary) from employee1 where deptid = did) > 1800;

(4) 基于上述 EMPLOYEES 表写出查询:查出个人工资高于其所在部门平均工资的员工, 列出这些员工的全部个人信息及该员工工资高出部门平均工资百分比。 (5 分) select employee1.*,(employee1.salary-t.avgSalary)*100/employee1.salary from employee1, (select deptid,avg(salary) avgSalary from employee1 group by deptid) as t where employee1.deptid = t.deptid and employee1.salary>t.avgSalary;

21、注册 Jdbc 驱动程序的三种方式

1. Class.forName(“com.mysql.jdbc.Driver”);//加载数据库驱动

Class.forName("com.mysql.jdbc.Driver");//加载数据库驱动  

String url="jdbc:mysql://localhost:3306/databasename";//数据库连接子协议  

Connection conn=DriverManager.getConnection(url,"username","password"); 

此方式由于参数为字符串,因此很容易修改,移植性强。

最常见的注册方式,也是推荐的方式。

第一种的好处在于能够在编译时不依赖于特定的JDBC Driver库,也就是减少了项目代码的依赖性,而且也很容易改造成从配置文件读取JDBC配置,从而可以在运行时动态更换数据库连接驱动。

private String dbUrl;private String driverClassName;private String userName;private String passWord;

​    

​    private Connection conn =null;private PreparedStatement ps =null;private ResultSet rs = null;

//初始化数据库@Overridepublic void init(ServletConfig config) throws ServletException {
​
​        //加载驱动try {this.dbUrl = config.getInitParameter("dbUrl");this.driverClassName = config.getInitParameter("driverClassName");this.userName = config.getInitParameter("userName");this.passWord = config.getInitParameter("passWord");

​            Class.forName(driverClassName);} catch (ClassNotFoundException e) {// TODO Auto-generated catch block

​            e.printStackTrace();}}
 

//数据库的查询public String getNickName(String openid) throws SQLException{

​        String nickName = "";//创建数据库链接

​        conn = DriverManager.getConnection(dbUrl, userName, passWord);

​        String sql = "select nickname from user where openid = ?";

​        ps = conn.prepareStatement(sql);

​        ps.setString(1, openid);

​        rs = ps.executeQuery();while (rs.next()) {

​            nickName = rs.getString("nickname");}

​        

​        //关闭链接

​        rs.close();

​        ps.close();

​        conn.close();return nickName;}

2.System.setProperty(“jdbc.drivers”,“com.mysql.jdbc.Driver”);

System.setProperty("jdbc.driver","com.mysql.jdbc.Driver");//系统属性指定数据库驱动  

String url="jdbc:mysql://localhost:3306/databasename";//数据库连接子协议  

Connection conn=DriverManager.getConnection(url,"username","password");  

可以同时导入多个jdbc驱动,中间用冒号“:”分开

比如System.setProperty(“jdbc.drivers”,“XXXDriver:XXXDriver:XXXDriver”);

这样就一次注册了三个数据库驱动

3. new com.mysql.jdbc.Driver() ;//创建driver对象,加载数据库驱动

new com.mysql.jdbc.Driver();//创建driver对象,加载数据库驱动  

String url="jdbc:mysql://localhost:3306/databasename";//数据库连接子协议  

Connection conn=DriverManager.getConnection(url,"username","password");  

这里不需要这样写DriverManager.registerDriver(new com.mysql.jdbc.Driver()),原因是com.mysql.jdbc.Driver类的静态代码快里面已经进行了修改的操作。我们通过Driver类的源码可以了解到,Driver类中就有一个静态的代码块,只要我们执行了Driver类中的静态代码块,并把驱动的实例放入到Drivers的一个数组列表中,我们再调用方法registerDrever就相当于又向drivers列表中放了一次driver驱动,虽然这并不影响我们程序,但是这样做实在是没有必要,还会影响程序的运行。

static {  

   try {  

​      java.sql.DriverManager.registerDriver(new Driver());  

   } catch (SQLException E) {throw new RuntimeException("Can't register driver!");  

   }  

}  

由new com.mysql.jdbc.Driver()可以知道,这里需要创建一个类的实例。创建类的实例就需要在java文件中将该类通过import导入,否则就会报错,即采用这种方式,程序在编译的时候不能脱离驱动类包,为程序切换到其他数据库带来麻烦

jdbc是使用桥的模式进行连接的。DriverManager就是管理数据库驱动的一个类,java.sql.Driver就 是一个提供注册数据库驱动的接口,而com.microsoft.sqlserver.jdbc.SQLServerDriver()是 java.sql.Driver接口的一个具体实现。

第二种与第三种注册的方法看起来更加的直接与好理解。第一种方法是通过Class把类先装载到java的虚拟机中,并没有创建Driver类的实例。
第一种与第二种方法可以脱离jdbc的驱动进行编译,第三种方法不可以的,它一定要有jdbc的驱动才可以通过编译,这样对我们的程序就有很多的不好之处,为程序换数据库会带来麻烦。

所以推荐使用第一种方法来注册驱动。

总结:推荐1,和2两种方式。
原因:3在编译时需要导入对应的lib。1,2不需要。

//数据库的查询
     public String getNickName(String openid) throws SQLException{
         String nickName = "";
         //创建数据库链接
         conn = DriverManager.getConnection(dbUrl, userName, passWord);
         String sql = "select nickname from user where openid = ?";
         ps = conn.prepareStatement(sql);
         ps.setString(1, openid);
         rs = ps.executeQuery();
         while (rs.next()) {
             nickName = rs.getString("nickname");
         }
         
         //关闭链接
         rs.close();
         ps.close();
         conn.close();
         
         return nickName;
     }

 

22、用 JDBC 如何调用存储过程

代码如下:

 package com.huawei.interview.lym; 
import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Types; 

public class JdbcTest { 

 /**   * @param args   */  public static void main(String[] args) {  
  // TODO Auto-generated method stub   Connection cn = null;   
  CallableStatement cstmt = null;   
  try {    //这里最好不要这么干,因为驱动名写死在程序中了  
    Class.forName("com.mysql.jdbc.Driver");    
    //实际项目中,这里应用DataSource数据,如果用框架,   
     //这个数据源不需要我们编码创建,我们只需Datasource ds = context.lookup()    
     //cn = ds.getConnection();       
     cn = DriverManager.getConnection("jdbc:mysql:///test","root","root");  
       cstmt = cn.prepareCall("{call insert_Student(?,?,?)}");    
       cstmt.registerOutParameter(3,Types.INTEGER);  
         cstmt.setString(1, "wangwu");   
          cstmt.setInt(2, 25);    cstmt.execute();    
          //get第几个,不同的数据库不一样,建议不写    
          System.out.println(cstmt.getString(3));   
          } catch (Exception e) {   
           // TODO Auto-generated catch block    e.printStackTrace(); 
             }   finally   { 
   /*try{cstmt.close();}catch(Exception e){}    try{cn.close();}catch(Exception e){}*/    try {     if(cstmt != null)      cstmt.close();     
   if(cn != null)       
      cn.close();    } catch (SQLException e) {    
       // TODO Auto-generated catch block     e.printStackTrace();    }   }  } 

23、JDBC 中的 PreparedStatement 相比 Statement 的好处

答:一个 sql 命令发给服务器去执行的步骤为:语法检查,语义分析,编译成内部指令,缓 存指令,执行指令等过程。 select * from student where id =3----缓存–xxxxx 二进制命令 select * from student where id =3----直接取-xxxxx 二进制命令 select * from student where id =4— -会怎么干?
如果当初是 select * from student where id =?— -又会怎么干? 上面说的是性能提高 可以防止 sql 注入。

25、Class.forName 的作用?为什么要用?

答:按参数中指定的字符串形式的类名去搜索并加载相应的类,如果该类字节码已经被加载 过,则返回代表该字节码的 Class 实例对象,否则,按类加载器的委托机制去搜索和加载该 类,如果所有的类加载器都无法加载到该类,则抛出 ClassNotFoundException。加载完这个 Class 字节码后,接着就可以使用 Class 字节码的 newInstance 方法去创建该类的实例对象了。

有时候,我们程序中所有使用的具体类名在设计时(即开发时)无法确定,只有程序运行时 才能确定,这时候就需要使用 Class.forName 去动态加载该类,这个类名通常是在配置文件 中配置的,例如,spring 的 ioc 中每次依赖注入的具体类就是这样配置的,jdbc 的驱动类名 通常也是通过配置文件来配置的,以便在产品交付使用后不用修改源程序就可以更换驱动类 名。

26、大数据量下的分页解决方法。

答:最好的办法是利用 sql 语句进行分页,这样每次查询出的结果集中就只包含某页的数据 内容。再 sql 语句无法实现分页的情况下,可以考虑对大的结果集通过游标定位方式来获取 某页的数据。 sql 语句分页,不同的数据库下的分页方案各不一样,下面是主流的三种数据库的分页 sql:

sql server: String sql = “select top " + pageSize + " * from students where id not in” +
“(select top " + pageSize * (pageNumber-1) + " id from students order by id)” + “order by id”;

mysql: String sql = "select * from students order by id limit " + pageSize*(pageNumber-1) + “,” + pageSize; oracle: String sql = "select * from " + (select ,rownum rid from (select * from students order by postime desc) where rid<=" +
pagesize
pagenumber + “) as t” + “where t>” + pageSize*(pageNumber-1);

27、用 JDBC 查询学生成绩单, 把主要代码写出来(考试概率极大).

Connection cn = null; PreparedStatement pstmt =null; Resultset rs = null; try { Class.forname(driveClassName); cn = DriverManager.getConnection(url,username,password); pstmt = cn.prepareStatement(“select score.* from score ,student “ + “where score.stuId = student.id and student.name = ?”); pstmt.setString(1,studentName); Resultset rs = pstmt.executeQuery(); while(rs.next()) { system.out.println(rs.getInt(“subject”) + “ ” + rs.getFloat(“score”) ); } }catch(Exception e){e.printStackTrace();} finally { if(rs != null) try{ rs.close() }catch(exception e){} if(pstmt != null) try{pstmt.close()}catch(exception e){} if(cn != null) try{ cn.close() }catch(exception e){} }

28、这段代码有什么不足之处?

try { Connection conn = …; Statement stmt = …;

ResultSet rs = stmt.executeQuery(“select * from table1”);

while(rs.next()) {

} } catch(Exception ex) { }

答:没有 finally 语句来关闭各个对象,另外,使用 finally 之后,要把变量的 定义放在 try 语句块的外面,以便在 try 语句块之外的 finally 块中仍可以访问 这些变量。

29、说出数据连接池的工作机制是什么?

J2EE 服务器启动时会建立一定数量的池连接,并一直维持不少于此数目的池连接。客 户端程序需要连接时,池驱动程序会返回一个未使用的池连接并将其表记为忙。如果 当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量有配置参数 决定。当使用的池连接调用完成后,池驱动程序将此连接表记为空闲,其他调用就可 以使用这个连接。 实现方式,返回的 Connection 是原始 Connection 的代理,代理 Connection 的 close 方法 不是真正关连接,而是把它代理的 Connection 对象还回到连接池中。

30、为什么要用 ORM? 和 JDBC 有何不一样?

orm 是一种思想,就是把 object 转变成数据库中的记录,或者把数据库中的记录转变成 objecdt,我们可以用 jdbc 来实现这种思想,其实,如果我们的项目是严格按照 oop 方 式编写的话,我们的 jdbc 程序不管是有意还是无意,就已经在实现 orm 的工作了。 现在有许多 orm 工具,它们底层调用 jdbc 来实现了 orm 工作,我们直接使用这些工具, 就省去了直接使用 jdbc 的繁琐细节,提高了开发效率,现在用的较多的 orm 工具是 hibernate。也听说一些其他 orm 工具,如 toplink,ojb 等。

四、数据库及 SQL 部分:(共 4 题:基础 3 道,中等难度 1 道)

106、有 3 个表(15 分钟):【基础】 Student 学生表 (学号,姓名,性别,年龄,组织部门) Course 课程表 (编号,课程名称) Sc 选课表 (学号,课程编号,成绩) 表结构如下

1)写一个 SQL 语句,查询选修了’计算机原理’的学生学号和姓名(3 分钟)

2)写一个 SQL 语句,查询’周星驰’同学选修了的课程名字(3 分钟)

3)写一个 SQL 语句,查询选修了 5 门课程的学生学号和姓名(9 分钟)

答:1)SQL 语句如下:

 select stu.sno, stu.sname from Student stu      where (select count(*) from sc where sno=stu.sno and cno =        (select cno from Course where cname='计算机原理')) != 0;    

2)SQL 语句如下:

   select cname from Course      where cno in ( select cno from sc where sno =                    (select sno from Student where sname='周星驰'));

3)SQL 语句如下:

 select stu.sno, stu.sname from student stu      where (select count(*) from sc where sno=stu.sno) = 5;  

107、有三张表,学生表 S,课程 C,学生课程表 SC,学生可以选修多门课程,一门课 程可以被多个学生选修,通过 SC 表关联。

1)写出建表语句;

2)写出 SQL 语句,查询选修了所有选修课程的学生;

3)写出 SQL 语句,查询选修了至少 5 门以上的课程的学生。

答:1)建表语句如下(mysql 数据库):

create table s(id integer primary key, name varchar(20));     
create table c(id integer primary key, name varchar(20));     
create table sc(      sid integer references s(id),       cid integer references c(id),       primary key(sid,cid)    );   

2)SQL 语句如下:

 select stu.id, stu.name from s stu where (select count(*) from sc where sid=stu.id)  = (select count(*) from c);      

3)SQL 语句如下:

 select stu.id, stu.name from s stu          where (select count(*) from sc where sid=stu.id)>=5;  

108、数据库表(Test)结构如下: ID NAME AGE MANAGER(所属主管人 ID) 106

​ A 30 104 109 B 19 104 104 C 20 111 107

D 35 109 112 E 25 120 119 F 45 NULL

要求:列出所有年龄比所属主管年龄大的人的 ID 和名字?
答:SQL 语句如下:

select employee.name from test employee where employee.age > (select manager.age from test manager where manager.id=employee.manager);  

五、JDBC 及 Hibernate:(基础 10 道,中等难度 2 道)

110、数据库,比如 100 用户同时来访,要采取什么技术解决?

答:可采用连接池。

111、什么是 ORM?

答:对象关系映射(Object—Relational Mapping,简称 ORM)是一种为了解决 面向对象与面向关系数据库存在的互不匹配的现象的技术;简单的说,ORM 是通 过使用描述对象和数据库之间映射的元数据,将 java 程序中的对象自动持久化 到关系数据库中;本质上就是将数据从一种形式转换到另外一种形式。

112、Hibernate 有哪 5 个核心接口?

答:Configuration 接口:配置 Hibernate,根据其启动 hibernate,创建 SessionFactory 对象; SessionFactory 接口:初始化 Hibernate,充当数据存储源的代理,创建 session 对象,sessionFactory 是线程安全的,意味着它的同一个实例可以被应 用的多个线程共享,是重量级、二级缓存; Session 接口:负责保存、更新、删除、加载和查询对象,是线程不安全的, 避免多个线程共享同一个 session,是轻量级、一级缓存; Transaction 接口:管理事务; Query 和 Criteria 接口:执行数据库的查询。

113、关于 hibernate: 【基础】

1)在 hibernate 中,在配置文件呈标题一对多,多对多的标签是什么;
2)Hibernate 的二级缓存是什么;
3)Hibernate 是如何处理事务的;
答:1)一对多的标签为 ;多对多的标签为;
2)sessionFactory 的缓存为 hibernate 的二级缓存;
3)Hibernate 的事务实际上是底层的 JDBC Transaction 的封装或者是 JTA Transaction 的封装;默认情况下使用 JDBCTransaction。

115、什么是重量级?什么是轻量级?

答:轻量级是指它的创建和销毁不需要消耗太多的资源,意味着可以在程序中经 常创建和销毁 session 的对象;重量级意味不能随意的创建和销毁它的实例,会 占用很多的资源。

117、事务处理?

答:Connection 类中提供了 3 个事务处理方法:
1.setAutoCommit(Boolean autoCommit):设置是否自动提交事务,默认为自动 提交事务,即为 true,通过设置 false 禁止自动提交事务;
2.commit():提交事务;
3. rollback():回滚事务。

118、Java中访问数据库的步骤?

Statement和PreparedStatement之间的区别?

答:Java 中访问数据库的步骤如下:
1)注册驱动; 2)建立连接;
3)创建 Statement; 4)执行 sql 语句;
5)处理结果集(若 sql 语句为查询语句);
6)关闭连接。 PreparedStatement 被创建时即指定了 SQL 语句,通常用于执行多次结构相 同的 SQL 语句。

120、JDBC,Hibernate 分页怎样实现?【中等难度】

答:方法分别为:

  1. Hibernate 的分页: Query query = session.createQuery("from Student"); query.setFirstResult(firstResult);//设置每页开始的记录号 query.setMaxResults(resultNumber);//设置每页显示的记录数 Collection students = query.list();
    
  2. JDBC 的分页:根据不同的数据库采用不同的 sql 分页语句

例如: Oracle 中的 sql 语句为:

"SELECT * FROM (SELECT a.*, rownum r FROM TB_STUDENT) WHERE r between 2 and 10"

查询从记录号 2 到记录号 10 之间的 所有记录

发布了33 篇原创文章 · 获赞 50 · 访问量 2672

猜你喜欢

转载自blog.csdn.net/qq_43107323/article/details/103693480