PHP+mysql常考题
来自《PHP程序员面试笔试宝典》,涵盖了近三年了各大型企业常考的PHP面试题,针对面试题提取出来各种面试知识也涵盖在了本书。
常考的mysql基础题
问题:设教务管理系统中有三个基本表:
学生信息表S(SNO, SNAME, AGE, SEX),其属性分别表示学号、学生姓名、年龄和性别。
选课信息表SC(SNO, CNO, SCGRADE),其属性分别表示学号、课程号和成绩。
课程信息表C(CNO, CNAME, CTEACHER),其属性分别表示课程号、课程名称和任课老师姓名。
1)把SC表中每门课程的平均成绩插入另外一个已经存在的表SC_C(CNO, CNAME, AVG_GRADE)中,其中AVG_GRADE表示的是每门课程的平均成绩。
INSERT INTO SC_C(CNO, CNAME, AVG_GRADE)
SELECT SC.CNO, C.CNAME, AVG(SCGRADE) FROM SC, C WHERE SC.CNO = C.CNO GROUP BY SC.CNO
2)规定女同学选修何昊老师的课程成绩都应该在80分以上(包含80分)。
ALERT TABLE SC, S, C
ADD CONSTRAINT GRADE CHECK(SCGRADE>=80)
WHERE SC.CNO=C.CNO AND SC.SNO=S.SNO AND C.CTEACHER='何昊' AND S.SEX=
"女"
3)从SC表中把何昊老师的女学生选课记录删除。
DELETE FROM SC WHERE CNO=(SELECT CNO FROM C WHERE C.CTEACHER ='何昊') AND SNO IN (SELECT SNO FROM S WHERE SEX='女')
4)找出没有选修过“何昊”老师讲授课程的所有学生姓名。
SELECT SNAME FROM S
WHERE NOT EXISTS(
SELECT * FROM SC,C WHERE SC.CNO=C.CNO AND CNAME='何昊' AND SC.SNO=S.SNO)
5)列出有两门以上(含两门)不及格课程(成绩小于60)的学生姓名及其平均成绩。
SELECT S.SNO,S.SNAME,AVG_SCGRADE=AVG(SC.SCGRADE)
FROM S,SC,(
SELECT SNO FROM SC WHERE SCGRADE<60 GROUP BY SNO
HAVING COUNT(DISTINCT CNO)>=2)A WHERE S.SNO=A.SNO AND SC.SNO = A.SNO
GROUP BY S.SNO,S.SNAME
6)列出既学过“1”号课程,又学过“2”号课程的所有学生姓名。
SELECT S.SNO,S.SNAME
FROM S,(SELECT SC.SNO FROM SC,C
WHERE SC.CNO=C.CNO AND C.CNAME IN('1','2')
GROUP BY SNO
HAVING COUNT(DISTINCT CNO)=2
)SC WHERE S.SNO=SC.SNO
7)列出“1”号课成绩比“2”号同学该门课成绩高的所有学生的学号。
SELECT S.SNO,S.SNAME
FROM S,(
SELECT SC1.SNO
FROM SC SC1,C C1,SC SC2,C C2
WHERE SC1.CNO=C1.CNO AND C1.NAME='1'
AND SC2.CNO=C2.CNO AND C2.NAME='2'
AND SC1.SCGRADE>SC2.SCGRADE
)SC WHERE S.SNO=SC.SNO
8)列出“1”号课成绩比“2”号课成绩高的所有学生的学号及其“1”号课和“2”号课的成绩。
SELECT S.SNO,S.SNAME,SC.[1号课成绩],SC.[2号课成绩]
FROM S,(
SELECT SC1.SNO,[1号课成绩]=SC1.SCGRADE,[2号课成绩]=SC2.SCGRADE
FROM SC SC1,C C1,SC SC2,C C2
WHERE SC1.CNO=C1.CNO AND C1.NAME='1'
AND SC2.CNO=C2.CNO AND C2.NAME='2'
AND SC1.SCGRADE>SC2.SCGRADE
)SC WHERE S.SNO=SC.SNO
UNION和UNION ALL有什么区别?
UNION在进行表求并集后会去掉重复的元素,所以会对所产生的结果集进行排序运算,删除重复的记录再返回结果。
而UNION ALL只是简单地将两个结果合并后就返回。因此,如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据。
从上面的对比可以看出,在执行查询操作的时候,UNION ALL要比UNION快很多,所以,如果可以确认合并的两个结果集中不包含重复的数据,那么最好使用UNION ALL。例如,如下有两个学生表Table1和Table2。
Table1 |
|
C1 |
C2 |
1 |
1 |
2 |
2 |
3 |
3 |
Table2 |
|
C1 |
C2 |
3 |
3 |
4 |
4 |
1 |
1 |
select * from Table1 union select * from Table2 的查询结果为
C1 |
C2 |
1 |
1 |
2 |
2 |
3 |
3 |
4 |
4 |
select * from Table1 union all select * from Table2 的查询结果为
C1 |
C2 |
1 |
1 |
2 |
2 |
3 |
3 |
3 |
3 |
4 |
4 |
1 |
1 |
3、什么是数据库三级封锁协议?
众所周知,基本的封锁类型有两种:排它锁(X锁)和共享锁(S锁)。所谓X锁是事务T对数据A加上X锁时,只允许事务T读取和修改数据A。所谓S锁是事务T对数据A加上S锁时,其他事务只能再对数据A加S锁,而不能加X锁,直到T释放A上的S锁。若事务T对数据对象A加了S锁,则T就可以对A进行读取,但不能进行更新(S锁因此又称为读锁),在T释放A上的S锁以前,其他事务可以再对A加S锁,但不能加X锁,从而可以读取A,但不能更新A。
在运用X锁和S锁对数据对象加锁时,还需要约定一些规则,例如,何时申请X锁或S锁、持锁时间、何时释放等,称这些规则为封锁协议(Locking Protocol)。对封锁方式规定不同的规则,就形成了各种不同的封锁协议。一般使用三级封锁协议,也称为三级加锁协议。该协议是为了保证正确的调度事务的并发操作。三级加锁协议是事务在对数据库对象加锁、解锁时必须遵守的一种规则。下面分别介绍这三级封锁协议。
一级封锁协议:事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放。事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK)。一级封锁协议可以防止丢失修改,并保证事务T是可恢复的。使用一级封锁协议可以解决丢失修改问题。在一级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,它不能保证可重复读和不读“脏”数据。
二级封锁协议:一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后方可释放S锁。二级封锁协议除防止了丢失修改,还可以进一步防止读“脏”数据。但在二级封锁协议中,由于读完数据后即可释放S锁,所以它不能保证可重复读。
三级封锁协议:一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放。三级封锁协议除防止了丢失修改和不读“脏”数据外,还进一步防止了不可重复读。
4、以下关于mysql_pconnect的说法中,正确的是( )。
A.与数据库进行多连接 B.与mysql_connect功能相同
C.与@mysql_connect功能相同 D.与数据库建立持久连接
参考答案:D。
分析:mysql_pconnect()函数打开一个到 MySQL 服务器的持久连接。
mysql_pconnect()和mysql_connect()非常相似,虽然只多了一个p,但它们有两个主要区别:当连接的时候本函数将先尝试寻找一个在同一个主机上用同样的用户名和密码已经打开的(持久)连接,如果找到,则返回此连接标识而不打开新连接。其次,当脚本执行完毕后到SQL服务器的连接不会被关闭,此连接将保持打开以备以后使用(mysql_close()不会关闭由mysql_pconnect()建立的连接)。所以,选项D正确。
【真题204】 PDO通过执行SQL查询与数据库进行交互,可以分为多种不同的策略,使用哪一种方法取决于你要做什么操作。如果向数据库发送DML语句,那么下面最合适的方式是( )。
A.使用PDO对象中的exec()方法
B.使用PDO对象中的query()方法
C.使用PDO对象中的prepare()和PDOStatement对象中的execute()两个方法结合
D.以上方式都可以
参考答案:A。
分析:PDO->exec()方法主要是针对没有结果集合返回的操作,例如INSERT、UPDATE、DELETE 等操作,它返回的结果是当前操作影响的列数。所以,选项A正确。
5、PHP的mysql系列函数中常用的遍历数据的函数是( )。
A.mysql_fetch_row,mysql_fetch_assoc,mysql_affetced_rows
B.mysql_fecth_row,mysql_fecth_assoc,mysql_affetced_rows
C.mysql_fetch_rows,mysql_fetch_array,mysql_fetch_assoc
D.mysql_fecth_row,mysql_fecth_array,mysql_fecth_assoc
参考答案:D。
分析:最常用的mysql系列函数常用的遍历数据函数有mysql_fetch_row、mysql_fetch_ array和mysql_fetch_assoc等三个函数,但不存在mysql_fetch_rows。
所以,本题的答案为D。
6、更改表字段名的标准语法为( )。
A.alter table 表名 add 字段字类型[first|after]
B.alter table 表名 drop 字段[first|after]
C.alter table 表名 change 原名新名新类型[first|after]
D.alter table 表名 modify 原名字段类型[first|after]
参考答案:C。
分析:修改表字段名的语法:alter table 表名change 原字段名新字段名类型;。
修改字段类型的语法:alter table 表名modify 字段名类型;。
增加一个字段:alter table 表名add column 字段名类型 not null(或default null);新增一个字段默认不为空(默认为空)。
删除一个字段:alter table 表名drop column 新字段名;。
Mysql常考高级部分
一、如何进行数据库优化?
数据库优化的过程可以使用以下的方法进行:
1)选取最适用的字段属性,尽可能减少定义字段长度,尽量把字段设置NOT NULL,例如'省份、性别',最好设置为ENUM。
2)使用连接(JOIN)来代替子查询。
① 删除没有任何订单客户:DELETE FROM customerinfo WHERE customerid NOT in(SELECT customerid FROM orderinfo)。
② 提取所有没有订单客户:SELECT FROM customerinfo WHERE customerid NOT in(SELECT customerid FROM orderinfo)。
③ 提高b的速度优化:SELECT FROM customerinfo LEFT JOIN orderid customerinfo. customerid=orderinfo.customerid WHERE orderinfo.customerid IS NULL。
3)使用联合(UNION)来代替手动创建的临时表。创建临时表:SELECT name FROM 'nametest' UNION SELECT username FROM 'nametest2'。
4)事务处理。保证数据完整性,例如添加和修改。同时,如果两者成立,则都执行,一者失败都失败:
mysql_query("BEGIN");
mysql_query("INSERT INTO customerinfo (name) VALUES ('$name1')";
mysql_query("SELECT * FROM 'orderinfo' where customerid=".$id");
mysql_query("COMMIT");
5)锁定表,优化事务处理。用一个SELECT语句取出初始数据,通过一些计算,用UPDATE语句将新值更新到表中。包含有WRITE关键字的LOCK TABLE语句可以保证在UNLOCK TABLES命令被执行之前,不会有其他的访问来对customerinfo表进行插入、更新或者删除的操作。
mysql_query("LOCK TABLE customerinfo READ, orderinfo WRITE");
mysql_query("SELECT customerid FROM 'customerinfo' where id=".$id);
mysql_query("UPDATE 'orderinfo' SET ordertitle='$title' where customerid=".$id);
mysql_query("UNLOCK TABLES");
6)使用外键,优化锁定表。把customerinfo里的customerid映射到orderinfo里的customerid,任何一条没有合法的customerid的记录不会写到orderinfo里。
CREATE TABLE customerinfo
(
customerid INT NOT NULL,
PRIMARY KEY(customerid)
)TYPE = INNODB;
CREATE TABLE orderinfo
(
orderid INT NOT NULL,
customerid INT NOT NULL,
PRIMARY KEY(customerid,orderid),
FOREIGN KEY (customerid) REFERENCES customerinfo
(customerid) ON DELETE CASCADE
)TYPE = INNODB;
注意:'ON DELETE CASCADE',该参数保证当customerinfo表中的一条记录删除的话同时也会删除order。
表中的该用户的所有记录,注意使用外键时要定义数据库引擎为INNODB。
二、选择正确的存储引擎?
在MySQL中有两个存储引擎:MyISAM和InnoDB,每个引擎都有利有弊。
MyISAM适合于一些需要大量查询的应用,但其对于有大量写操作的支持并不是很好。甚至只是需要update一个字段,整个表都会被锁起来,而其他进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。
InnoDB 的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比 MyISAM 还慢。但是它支持“行锁”,于是在写操作比较多的时候,会更优秀。并且,它还支持更多的高级应用,例如事务。
三、【真题231】 用什么方法检查PHP脚本的执行效率(通常是脚本执行时间)和数据库SQL的效率(通常是数据库query时间),并定位和分析脚本执行和数据库查询的瓶颈所在?
参考答案:检查PHP脚本的执行效率的方法如下:可以在检查的代码开头记录一个时间,然后在代码的结尾也记录一个时间,结尾时间减去开头时间取这个时间的差值,从而检查PHP的脚本执行效率,记录时间可以使用microtime()函数。
检查数据库SQL的效率的方法如下:可以通过explain显示MySQL如何使用索引来处理select语句及连接表,帮助选择更好的索引和写出更优化的查询语句。然后启用slow query log记录慢查询,通过查看SQL的执行时间和效率来定位分析脚本执行的问题和瓶颈所在。
四、 以下代码的运行结果为( )。
<?php
mysql_connect('localhost','root',"");
$result = mysql_query("SELECT id,name FROM tb1");
while($row = mysql_fetch_array($result,MySQL_ASSOC)){
echo' ID:' .$row[0].' Name:' .$row[];
}
?>
A.报错 B.循环换行打印全部记录
C.无任何结果 D.只打印第一条记录
参考答案:A。
分析:因为代码中没有指明要操作的数据库名,所以会报错。
所以,本题的答案为A。
五、考虑如下数据表和查询,如何添加索引能提高查询速度?( )
CREATE TABLE MYTABLE (
ID INT,
NAME VARCHAR (100),
ADDRESS1 VARCHAR (100),
ADDRESS2 VARCHAR (100),
ZIPCODE VARCHAR (10),
CITY VARCHAR (50),
PROVINCE VARCHAR (2)
)
SELECT ID, VARCHAR FROM MYTABLE WHERE ID BETWEEN 0 AND 100 ORDER BY NAME, ZIPCODE
A.给 ID 添加索引
B.给 NAME 和 ADDRESS1 添加索引
C.给ID 添加索引,然后给 NAME 和 ZIPCODE 分别添加索引
D.给ZIPCODE 和 NAME 添加索引
参考答案:C。
分析:给ID字段设置索引能提高 where 条件执行的效率,给 NAME 和 ZIPCODE设索引则能使排序更快。
六、 如何高效操作MySQL?
MySQL对于PHP甚至是所有开发者都是非常基础和重要的模块,对于熟悉的LAMP体系架构,我们需要构建稳定可靠的系统,数据库环境是必不可少和关键的地方。在使用MySQL过程中,有以下建议:
1)使用InnoDB数据库引擎。MySQL常用的有MyISAM和InnoDB两种,MyISAM不支持外键约束或者事务处理。当插入或更新一条记录时,整个数据表都被锁定了,随着使用量的增加,性能会非常差。
2)使用MySQLi面向对象的数据库操作方法。PHP5支持了面向对象的访问数据库方法。具体的优点前面已经讲过,此处不再赘述。
3)对于用户输入进行验证。用户输入的内容是一个很大的变量之一,要防止SQL注入或黑客登录等安全隐患。
4)MySQL未使用utf-8字符集。utf-8字符集解决了很多国际化的问题,需要尽量使用此字符集,防止字符的问题出现。
5)通过SQL来替代PHP逻辑处理。通常来说,执行一个查询比在结果中使用PHP语言来迭代处理更有效率。所以,需要尽量通过SQL来替代PHP逻辑处理,提高效率。
6)优化数据库查询。几乎绝大部分PHP性能问题都是数据库引起的,经常出现的慢查询等SQL查询问题可能会让系统崩溃。需要对数据库进行优化查询。
7)要正确使用数据类型。
MySQL提供了诸如numeric、string和date等的数据类型。如果想存储一个时间,那么使用date或者datetime类型。如果这个时候用integer或者string类型,那么将会使得SQL查询非常复杂。
很多人倾向于擅自自定义一些数据的格式,例如,使用string来存储序列化的PHP对象。这样的话数据库管理起来可能会变得简单些,但会使得MySQL成为一个糟糕的数据存储而且之后很可能会引起故障。
8)不要在查询中使用“*”。这会返回表中所有数据,这是懒惰的表现,会在降低效率和出错概率上都大大提高。
9)合理使用索引技术。不使用或过度使用索引都会造成性能降低。如果在每个字段都加了索引,那么当执行修改操作时,索引都需要重新生成,会对性能影响较大。不使用索引,同样会造成全表查询,降低效率。使用索引一般性原则是这样的:select语句中的任何一个where子句表示的字段都应该使用索引。
10)记得备份。数据库必须进行备份,常见的有主从、主主数据库等系统架构形式。
七、【真题222】 以下说法正确的是( )。
A.使用索引能加快插入数据的速度
B.良好的索引策略有助于防止跨站攻击
C.应当根据数据库的实际应用合理设计索引
D.删除一条记录将导致整个表的索引被破坏
参考答案:C。
分析:索引的作用主要是帮助数据库快速查找到对应的数据,并不能加快插入数据的速度,所以,选项A错误。
索引不能够帮助防止跨站攻击,所以,选项B错误。
创建合理的索引需要分析数据库的实际用途并找出它的弱点。优化脚本中的冗余查询同样也能提高数据库效率。索引是占用物理空间的,所以在实际的应用中是要合理设计使用索引的。所以,选项C正确。
索引是一种表结构,删除一条数据也不会影响到整个表的索引,并且索引不一定是数字,也可以是字符串。所以,选项D错误。
八、如何对MySQL的系统内核优化?
大多数MySQL都部署在Linux系统上,所以,操作系统的一些参数也会影响到MySQL性能,以下参数的设置可以对Linux内核进行适当优化。
l net.ipv4.tcp_fin_timeout = 30 #TIME_WAIT超时时间,默认是60s
l net.ipv4.tcp_tw_reuse = 1 #1表示开启复用,允许TIME_WAIT socket重新用于新的TCP连接,0表示关闭
l net.ipv4.tcp_tw_recycle = 1 #1表示开启TIME_WAIT socket快速回收,0表示关闭
l net.ipv4.tcp_max_tw_buckets = 4096 #系统保持TIME_WAIT socket最大数量,如果超出这个数,系统将随机清除一些TIME_WAIT并打印警告信息
l net.ipv4.tcp_max_syn_backlog = 4096 #进入SYN队列最大长度,加大队列长度可容纳更多的等待连接
在Linux系统中,如果进程打开的文件句柄数量超过系统默认值1024,就会提示“too many files open”信息,所以,要调整打开文件句柄限制。
# vi /etc/security/limits.conf #加入以下配置,*代表所有用户,也可以指定用户,重启系统生效
* soft nofile 65535
* hard nofile 65535
# ulimit -SHn 65535 #立刻生效
九、什么是数据库权限?
关于MySQL的权限简单的理解就是MySQL允许用户做权利以内的事情,不可以越界。例如只允许一个用户执行SELECT操作,那么它就不能执行UPDATE操作;只允许一个用户从某台机器上连接MySQL,那么它就不能从除那台机器以外的其他机器连接MySQL。
那么MySQL的权限是如何实现的呢?这就要说到MySQL的两阶段的验证,下面详细来介绍。第一阶段:服务器首先会检查是否允许连接。因为创建用户的时候会加上主机限制,可以限制成本地、某个IP、某个IP段以及任何地方等,只允许你从配置的指定地方登录。后面在实战的时候会详细介绍关于主机的限制。第二阶段:如果能连接,那么 MySQL 会检查发出的每个请求,看是否有足够的权限实施它。例如,要更新某个表或者查询某个表,MySQL会检查对某个表或者某个列是否有权限。再例如,要运行某个存储过程,MySQL会检查对存储过程是否有执行权限等。
MySQL权限控制原则:
1)只授予能满足需要的最小权限,防止用户干坏事。例如,用户只是需要查询,那就只给SELECT权限就可以了,不要给用户赋予UPDATE、INSERT或者DELETE权限。
2)创建用户的时候限制用户的登录主机,一般是限制成指定IP或者内网IP段。
3)初始化数据库的时候删除没有密码的用户。安装完数据库的时候会自动创建一些用户,这些用户默认没有密码。
4)为每个用户设置满足密码复杂度要求的密码。
5)定期清理不需要的用户。回收权限或者删除用户。
示例1:创建一个只允许从本地登录的超级用户feihong,并允许将权限赋予别的用户,密码为123。
GRANT ALL PRIVILEGES ON *.* TO feihong@'localhost' IDENTIFIED BY '123' WITH GRANT OPTION;
GRANT命令说明:
ALL PRIVILEGES 是表示所有权限,也可以使用SELECT、UPDATE等权限。
ON 用来指定权限针对哪些库和表。
*.* 中前面的*号用来指定数据库名,后面的*号用来指定表名。
TO 表示将权限赋予某个用户。
feihong@'localhost' 表示feihong用户,@后面接限制的主机,可以是IP、IP段、域名以及%,%表示任何地方。注意:这里%有的版本不包括本地,以前碰到过给某个用户设置了%允许任何地方登录,但是在本地登录不了,这个和版本有关系,遇到这个问题再加一个localhost的用户就可以了。
IDENTIFIED BY 指定用户的登录密码。
WITH GRANT OPTION 这个选项表示该用户可以将自己拥有的权限授权给别人。注意:经常有人在创建操作用户的时候不指定WITH GRANT OPTION选项,导致后来该用户不能使用GRANT命令创建用户或者给其他用户授权。
备注:可以使用GRANT重复给用户添加权限,权限叠加,例如,先给用户添加了一个SELECT权限,然后又给用户添加了一个INSERT权限,那么该用户就同时拥有了SELECT和INSERT权限。
示例2:创建一个网站用户(程序用户)。
创建一个一般的程序用户,这个用户可能只需要SELECT、INSERT、UPDATE、DELETE、CREATE TEMPORARY TABLES等权限,如果有存储过程还需要加上EXECUTE权限,那么一般是指定内网网段192.168.100网段。
GRANT USAGE,SELECT, INSERT, UPDATE, DELETE, SHOW VIEW ,CREATE TEMPORARY TABLES,EXECUTE ON 'test'.* TO webuser@'192.168.100.%' IDENTIFIED BY 123';
示例3:创建一个普通用户(仅有查询权限)。
GRANT USAGE,SELECT ON 'test'.* TO public@'192.168.100.%' IDENTIFIED BY 'test';
示例4:查看权限。
使用如下命令可以方便地查看到某个用户的权限:
SHOW GRANTS FOR 'webuser'@'192.168.100.%';
示例5:删除用户。
注意删除用户不要使用DELETE直接删除,因为使用DELETE删除后用户的权限并未删除,新建同名用户后又会继承以前的权限。正确的做法是使用DROP USER命令删除用户,例如,要删除'webuser'@'192.168.100.%'用户采用如下命令:
DROP USER 'webuser'@'192.168.100.%';
示例6:回收权限。
将前面创建的webuser用户的DELETE权限回收,使用如下命令:
REVOKE DELETE ON test.* FROM 'webuser'@'192.168.100.%';
十、【真题220】 MySQL数据库中的字段类型varchar和char的主要区别是什么?哪种字段的查找效率要高,为什么?
参考答案:varchar是变长,节省存储空间,char是固定长度。查找效率char型比varchar快,因为varchar是非定长,必须先查找长度,然后进行数据的提取,比char定长类型多了一个步骤,所以效率低一些。
购买链接:京东购买
题目来自《PHP程序员面试笔试宝典》,里面涵盖了近三年了各大型企业常考的PHP面试题,针对面试题提取出来各种面试知识也涵盖在了本书。
更多PHP面试笔试真题可以浏览:www.shuaiqi100.com
更多有趣有料的PHP面试笔试资料可以关注:“琉忆编程库”
或者浏览:www.shuaiqi100.com 获取。
PHP程序员面试笔试宝典下载:https://pan.baidu.com/s/1-ES2ZI3z5Lhv-zTKFmJDSQ