本文内容来自牛客网上数据库SQL实战,题目描述地址: https://www.nowcoder.com/ta/sql
题目顺序按照热度指数排序的,部分题目的思路来自该题目下热门讨论内容。
1.查找最晚入职员工的所有信息
思路:找出最晚入职的员工,即入职的时间是最大的,使用子查询将该条件作为过滤条件。1
2
SELECT * FROM employees
WHERE hire_date = (SELECT MAX (hire_date) FROM employees);
其他思路:把入职时间降序排序,那么排在最前面的就是入职时间最大的,也是最晚入职的员工,然后取该排序序列的第一个。1
2
3
SELECT * FROM employees
ORDER BY hire_date DESC
LIMIT 1
但是这个思路不太严谨,摘自该题目后fsy351的解释:最晚入职的当天未必就一个人,也许有多人,使用排序并限制得只能取得指定数量的结果
2.查找入职员工时间排名倒数第三的员工所有信息
思路:把入职时间倒序排序,然后使用LIMIT关键字。1
2
3
SELECT * FROM employees
ORDER BY hire_date DESC
LIMIT 2 ,1 ;
LIMIT m,n : 表示从第m+1条开始,取n条数据; LIMIT n : 表示从第0条开始,取n条数据,是limit(0,n)的缩写。
但是这样写不太严谨,比如有多个员工在同一天入职,那么应该按入职日期进行分组,将多个入职日期相同的分为一组,再排序,这样入职时间倒数第三的员工就都可以查出来了。1
2
3
4
5
SELECT * FROM employees
WHERE hire_date = (
SELECT DISTINCT hire_date FROM employees
ORDER BY hire_date DESC LIMIT 2 ,1
);
3.查找各个部门当前(to_date=’9999-01-01’)领导当前薪水详情以及其对应部门编号dept_no
思路:把两张表关联起来,设定条件为to_date=9999-01-011
2
3
SELECT sa.*,dm.dept_no
FROM salaries AS sa INNER JOIN dept_manager AS dm ON dm.emp_no = sa.emp_no
WHERE sa.to_date = '9999-01-01' AND dm.to_date='9999-01-01' ;
4.查找所有已经分配部门的员工的last_name和first_name
1
2
SELECT em.last_name, em.first_name, de.dept_no
FROM dept_emp AS de INNER JOIN employees AS em ON de.emp_no = em.emp_no;
5.查找所有员工的last_name和first_name以及对应部门编号dept_no,也包括展示没有分配具体部门的员工
思路:以员工为主表,这样即使没有分配部门的员工也可展示出来。1
2
SELECT em.last_name, em.first_name, de.dept_no
FROM employees AS em LEFT JOIN dept_emp AS de ON de.emp_no = em.emp_no;
6.查找所有员工入职时候的薪水情况,给出emp_no以及salary, 并按照emp_no进行逆序
第一次我的错误的写法:1
2
3
SELECT ep.emp_no, sa.salary
FROM employees AS ep INNER JOIN salaries AS sa ON ep.emp_no = sa.emp_no
ORDER BY ep.emp_no DESC ;
刚开始没有明白错在哪里,看讨论区后才知道读题不认真,要求的是查入职时候的薪水,而在salaries表中,每个号码为emp_no的员工会在不同时间段涨薪,这样一个emp_no就对应了多个salary。
因此按照题目要求的查入职时候的薪水,添加条件sa.from_date = ep.hire_date,正确写法:1
2
3
4
SELECT ep.emp_no, sa.salary
FROM employees AS ep INNER JOIN salaries AS sa ON ep.emp_no = sa.emp_no
WHERE sa.from_date = ep.hire_date
ORDER BY ep.emp_no DESC ;
7.查找薪水涨幅超过15次的员工号emp_no以及其对应的涨幅次数t
思路:把员工分组,使用COUNT()函数计算涨幅次数,使用HAVING条件过滤涨幅次数大于15的,
1
2
3
4
SELECT emp_no, COUNT (salary) AS t
FROM salaries
GROUP BY emp_no
HAVING t>15 ;
这样虽然可以通过,但是并不完全正确。COUNT()只是统计次数,并不能判定“涨薪”。 比如emp_no为10002的员工: +——–+——–+————+————+ | emp_no | salary | from_date | to_date | +——–+——–+————+————+ | 10002 | 72527 | 1996-08-03 | 1997-08-03 | | 10002 | 72527 | 1997-08-03 | 1998-08-03 | | 10002 | 72527 | 1998-08-03 | 1999-08-03 | | 10002 | 72527 | 1999-08-03 | 2000-08-02 | | 10002 | 72527 | 2000-08-02 | 2001-08-02 | | 10002 | 72527 | 2001-08-02 | 9999-01-01 |
COUNT()统计次数有6次,但是涨薪次数为0次。 因此还应该加入下次的salary比本次的salary高,才统计为一次涨薪
1
2
3
4
5
6
SELECT s1.emp_no, COUNT (s1.emp_no) AS t
FROM salaries AS s1 INNER JOIN salaries AS s2
ON s1.emp_no = s2.emp_no
WHERE s1.salary < s2.salary AND s1.to_date = s2.from_date
GROUP BY emp_no
HAVING COUNT (s1.emp_no)>15 ;
不过这个题目这样解没有给通过
8.找出所有员工当前(to_date=’9999-01-01’)具体的薪水salary情况,对于相同的薪水只显示一次,并按照逆序显示
思路:使用DISTINCT去重,DESC逆序排列
1
2
3
4
SELECT DISTINCT salary
FROM salaries
WHERE to_date = '9999-01-01'
ORDER BY salary DESC ;
其他思路: 如果是针对大量数据的去重,可以使用GROUP BY解决去重,1
2
3
4
5
SELECT salary
FROM salaries
WHERE to_date = '9999-01-01'
GROUP BY salary
ORDER BY salary DESC ;
9. 获取所有部门当前manager的当前薪水情况,给出dept_no, emp_no以及salary,当前表示to_date=’9999-01-01’
思路:将两表联结,取出题目需要的。注意要求的是当前manager的当前薪水情况,两个当前条件都要满足,这里又错了一遍。。1
2
3
SELECT dm.dept_no, dm.emp_no, sa.salary
FROM dept_manager AS dm INNER JOIN salaries AS sa ON dm.emp_no = sa.emp_no
WHERE dm.to_date = '9999-01-01' AND sa.to_date = '9999-01-01' ;
10. 获取所有非manager的员工emp_no
思路:先选出所有manager员工的emp_no,再用NOT IN过滤掉这些emp_no
1
2
3
4
5
SELECT emp_no FROM employees
WHERE emp_no NOT IN (
SELECT emp_no
FROM dept_manager
);
其他思路:1
2
3
4
5
6
7
SELECT em.emp_no
FROM employees AS em
WHERE NOT EXISTS (
SELECT emp_no
FROM dept_manager AS dm
WHERE em.emp_no = dm.emp_no
);
其他思路:1
2
3
4
SELECT em.emp_no
FROM employees AS em LEFT JOIN dept_manager AS dm
ON em.emp_no = dm.emp_no
WHERE dm.dept_no IS NULL ;
11.获取所有员工当前的manager,如果当前的manager是自己的话结果不显示,当前表示to_date=’9999-01-01’。结果第一列给出当前员工的emp_no,第二列给出其manager对应的manager_no。 思路:员工的当前管理者:那么员工所在部门dept_no
与管理者所在部门dept_no
应该是相同的,通过这点将dept_emp表和dept_manager表联结起来,然后判断当前的manager是自己,可以通过员工表中的emp_no和管理者表中emp_no,这两个编号相同即当前的manager是自己,最后设定当前时间。还需要注意员工的manager对应的manager_no在表中是没有的,这个编号就是管理表中的emp_no,把他起个别名manager_no就可以。
1
2
3
SELECT de.emp_no,dm.emp_no AS manager_no
FROM dept_emp AS de INNER JOIN dept_manager AS dm ON de.dept_no = dm.dept_no
WHERE de.to_date='9999-01-01' AND dm.to_date = '9999-01-01' AND de.emp_no != dm.emp_no;
12.获取所有部门中当前员工薪水最高的相关信息,给出dept_no, emp_no以及其对应的salary
1
2
3
4
5
6
SELECT de.dept_no, MAX (sa.salary) AS salary
FROM salaries AS sa INNER JOIN dept_emp AS de
ON sa.emp_no = de.emp_no
WHERE de.to_date='9999-01-01' AND sa.to_date='9999-01-01'
GROUP BY de.dept_no
ORDER BY de.dept_no;
13.从titles表获取按照title进行分组,每组个数大于等于2,给出title以及对应的数目t。
1
2
3
4
SELECT title, COUNT (title) AS t
FROM titles
GROUP BY title
HAVING COUNT (title) >= 2 ;
14.从titles表获取按照title进行分组,每组个数大于等于2,给出title以及对应的数目t。注意对于重复的title进行忽略。 思路: 这个题和13题的区别在于,对重复的title进行去重,比如题目给的数据中, +——–+——————–+————+————+ | emp_no | title | from_date | to_date | +——–+——————–+————+————+ | 10010 | Engineer | 1996-11-24 | 9999-01-01 | | 10010 | Engineer | 1996-11-24 | 9999-01-01 | +——–+——————–+————+————+ 重复的title指的是emp_no中重复的,因此对emp_no去重1
2
3
4
SELECT title, COUNT (DISTINCT emp_no) AS t
FROM titles
GROUP BY title
HAVING t >= 2 ;
15.查找employees表所有emp_no为奇数,且last_name不为Mary的员工信息,并按照hire_date逆序排列
1
2
3
4
SELECT *
FROM employees
WHERE emp_no % 2 = 1 AND last_name != 'Mary'
ORDER BY hire_date DESC ;
16. 统计出当前各个title类型对应的员工当前(to_date=’9999-01-01’)薪水对应的平均工资。结果给出title以及平均工资avg。
1
2
3
4
SELECT title, AVG (sa.salary) AS avg
FROM titles AS ti INNER JOIN salaries AS sa ON ti.emp_no = sa.emp_no
WHERE sa.to_date = '9999-01-01' AND ti.to_date = '9999-01-01'
GROUP BY ti.title;
17.获取当前(to_date=’9999-01-01’)薪水第二多的员工的emp_no以及其对应的薪水salary
我开始是这样写的,也通过了1
2
3
4
5
SELECT emp_no,salary
FROM salaries
WHERE to_date = '9999-01-01'
ORDER BY salary DESC
LIMIT 1 ,1 ;
但是看评论时候才发现不严谨,自己想的不够周全,题目要的是薪水第二多的,假如在公司中,当前薪水第一多(设为100万)的有3个人,薪水第二多的有1个人(设为80万),那么按照上面的排序,结果是:
而LIMIT 1,1
选到的是第二个,是100万,但是题目要的是选出80万的,因此,应该对薪水同样多的进行去重,正确的写法:1
2
3
4
5
6
7
8
9
10
SELECT emp_no,salary
FROM salaries
WHERE salary = (
SELECT salary FROM salaries
WHERE to_date = '9999-01-01'
GROUP BY salary
ORDER BY salary DESC
LIMIT 1 ,1
)
AND to_date = '9999-01-01' ;
18.查找当前薪水(to_date=’9999-01-01’)排名第二多的员工编号emp_no、薪水salary、last_name以及first_name,不准使用order by
先用MAX()函数选出当前最高者:SELECT MAX(salary) FROM salaries WHERE to_date='9999-01-01'
然后在小于最高者的薪水中再选出最高者,即当前排名第二高的:1
2
3
SELECT MAX (salary) FROM salaries
WHERE salary < (SELECT MAX (salary) FROM salaries WHERE to_date ='9999-01-01' )
AND to_date = '9999-01-01'
最后,联结employees表,选出编号,姓名:1
2
3
4
5
6
7
8
SELECT emp.emp_no,sa.salary,emp.last_name,emp.first_name
FROM salaries AS sa INNER JOIN employees AS emp
ON sa.emp_no = emp.emp_no
WHERE to_date ='9999-01-01' AND salary = (
SELECT MAX (salary) FROM salaries
WHERE salary < (SELECT MAX (salary) FROM salaries WHERE to_date ='9999-01-01' )
AND to_date = '9999-01-01'
);
19.查找所有员工的last_name和first_name以及对应的dept_name,也包括暂时没有分配部门的员工 思路:将三张表联结起来,因为要包括暂时没有分配部门的员工,使用LEFT JOIN
1
2
3
SELECT emp.last_name, emp.first_name, dpm.dept_name
FROM employees AS emp LEFT JOIN dept_emp AS dep ON emp.emp_no = dep.emp_no
LEFT JOIN departments AS dpm ON dep.dept_no = dpm.dept_no;
20.查找员工编号emp_no为10001其自入职以来的薪水salary涨幅值growth
可以通过但是不严谨的写法:
1
2
3
SELECT (MAX (salary)-MIN (salary)) AS growth
FROM salaries
WHERE emp_no = 10001 ;
如果考虑到工资不是一直涨的,比如入职时工资1万,两年后3万,再过半年降成2.5万,现在工资是2.7万,那么入职以来薪水涨幅值是1.7万,而不是3-1=2万 因此,应该将最近一次工资减去入职时候第一次的工资,得到的才是涨幅值1
2
3
4
SELECT (
(SELECT salary FROM salaries WHERE emp_no = 10001 ORDER BY to_date DESC LIMIT 1 ) -
(SELECT salary FROM salaries WHERE emp_no = 10001 ORDER BY to_date ASC LIMIT 1 )
) AS growth;
21.查找所有员工自入职以来的薪水涨幅情况,给出员工编号emp_no以及其对应的薪水涨幅growth,并按照growth进行升序 思路:本题是求所有员工的,因此可以考虑把薪水salaries表做两份,一份表示当前的,用来得到当前的薪水sa1.salary,另一份是入职时的,用来得到入职时候的薪水sa2.salary。当前这个条件可以设为to_date='9999-01-01'
,入职时候的条件可以设为emp.hire_date = sa2.from_date
,那么涨幅情况就可以用当前薪水-入职时的薪水:sa1.salary-sa2.salary
1
2
3
4
5
6
SELECT emp.emp_no, (sa1.salary-sa2.salary) AS growth
FROM employees AS emp INNER JOIN salaries AS sa1
ON emp.emp_no = sa1.emp_no AND sa1.to_date='9999-01-01'
INNER JOIN salaries AS sa2
ON emp.emp_no = sa2.emp_no AND emp.hire_date = sa2.from_date
ORDER BY growth ASC ;
22.统计各个部门对应员工涨幅的次数总和,给出部门编码dept_no、部门名称dept_name以及次数sum
思路:可以通过牛客网提交但是不周全的方案:三表联结,对部分分组,使用COUNT()统计次数
1
2
3
4
SELECT dm.dept_no, dm.dept_name, COUNT (salary) AS sum
FROM salaries AS sa INNER JOIN dept_emp AS de ON sa.emp_no = de.emp_no
INNER JOIN departments AS dm ON dm.dept_no = de.dept_no
GROUP BY dm.dept_no, dm.dept_name;
和前面一道涨幅次数超过15次的题目很像,需要判定是涨的记录,而不是不变或降的记录。因此我这样写的,但是没有通过提交。
1
2
3
4
5
6
SELECT dm.dept_no, dm.dept_name, COUNT (sa1.emp_no) AS sum
FROM salaries AS sa1 INNER JOIN salaries AS sa2 ON sa1.emp_no = sa2.emp_no
INNER JOIN dept_emp AS de ON sa1.emp_no = de.emp_no
INNER JOIN departments AS dm ON dm.dept_no = de.dept_no
WHERE sa1.salary<sa2.salary AND sa1.to_date = sa2.from_date
GROUP BY dm.dept_no, dm.dept_name;
23.对所有员工的当前(to_date=’9999-01-01’)薪水按照salary进行按照1-N的排名,相同salary并列且按照emp_no升序排列
思路:如果支持窗口函数,根据相同的salary并列,排名为:1,2,2,3,3,3,4这样的顺序,那么可以使用dense_rank()窗口函数。
1
2
3
4
SELECT emp_no, salary, dense_rank () OVER (ORDER BY salary DESC ) AS rank
FROM salaries
WHERE to_date ='9999-01-01'
ORDER BY salary DESC , emp_no ASC ;
但是如果不支持窗口函数,要对工资进行1-N的排名,不用窗口函数对单个表显示排名,我一开始没有思路,,后来看了讨论区,理解了热评里的实现过程。把salary表做成两份,进行对比,一份是原表sa1,另一份是排名用的表sa2。如果某工资排名第五,那么就是说有比他工资高(sa1.salary <= sa2.salary
)的有4份,如果某工资排名第二,那么就是说有比他工资高有1份,可以使用COUNT()来统计比某份工资高的个数来当做排名。 还需要注意去重,比如s1.salary=94409时,有3个s2.salary(分别为94692,94409,94409)大于等于它,但由于94409重复,利用COUNT(DISTINCT s2.salary)去重可得工资为94409的rank等于2 最后排名时,工资salary逆序排,最大的为第一名,emp_no升序排列
1
2
3
4
5
6
SELECT sa1.emp_no, sa1.salary, COUNT (DISTINCT sa2.salary) AS rank
FROM salaries AS sa1 INNER JOIN salaries AS sa2 ON sa2.emp_no = sa2.emp_no
WHERE sa1.to_date = '9999-01-01' AND sa2.to_date = '9999-01-01'
AND sa1.salary <= sa2.salary
GROUP BY sa1.emp_no,sa1.salary
ORDER BY sa1.salary DESC , sa1.emp_no ASC ;
24.获取所有非manager员工当前的薪水情况,给出dept_no、emp_no以及salary ,当前表示to_date=’9999-01-01’
1
2
3
4
5
6
SELECT de.dept_no, sa.emp_no, sa.salary
FROM dept_emp AS de INNER JOIN salaries AS sa
ON sa.emp_no = de.emp_no AND sa.to_date='9999-01-01'
WHERE de.emp_no NOT IN (
SELECT emp_no FROM dept_manager WHERE to_date ='9999-01-01'
);
25.获取员工其当前的薪水比其manager当前薪水还高的相关信息,当前表示to_date=’9999-01-01’,结果第一列给出员工的emp_no,第二列给出其manager的manager_no,第三列给出该员工当前的薪水emp_salary,第四列给该员工对应的manager当前的薪水manager_salary
来自本题热评中wasrehpic的思路: 本题主要思想是创建两张表(一张记录当前所有员工的工资,另一张只记录部门经理的工资)进行比较,具体思路如下: 1、先用INNER JOIN连接salaries和demp_emp,建立当前所有员工的工资记录sde 2、再用INNER JOIN连接salaries和demp_manager,建立当前所有经理的工资记录sdm 3、最后用限制条件sem.dept_no = sdm.dept_no AND sem.salary > sdm.salary找出同一部门中工资比经理高的员工,并根据题意依次输出emp_no、manager_no、emp_salary、manager_salary1
2
3
4
5
6
7
8
9
10
11
12
SELECT sde.emp_no AS emp_no, sdm.emp_no AS manager_no, sde.salary AS emp_salary, sdm.salary AS manager_salary
FROM (
SELECT sa.salary, sa.emp_no, de.dept_no
FROM salaries AS sa INNER JOIN dept_emp AS de
ON sa.emp_no = de.emp_no AND sa.to_date='9999-01-01'
)AS sde,
(
SELECT sa.salary, sa.emp_no, dm.dept_no
FROM salaries AS sa INNER JOIN dept_manager AS dm
ON sa.emp_no = dm.emp_no AND sa.to_date='9999-01-01'
) AS sdm
WHERE sde.dept_no = sdm.dept_no AND sde.salary > sdm.salary;
26.汇总各个部门当前员工的title类型的分配数目,结果给出部门编号dept_no、dept_name、其当前员工所有的title以及该类型title对应的数目count 思路:首先需要对各个部门进行分组,分组后得到的每组是某个部门的员工。员工可能有多个类型的title,要统计某类型title对应的数目,还需要对title进行分组,这样两次分组后,只有同一部门且同一title的才是一个组。当前员工的当前title,员工和title都需要加to_date的限制。
1
2
3
4
5
6
SELECT de.dept_no, dm.dept_name, t.title, COUNT (t.title)
FROM departments AS dm INNER JOIN dept_emp AS de
ON dm.dept_no = de.dept_no AND de.to_date='9999-01-01'
INNER JOIN titles AS t
ON de.emp_no = t.emp_no AND t.to_date='9999-01-01'
GROUP BY de.dept_no, t.title;
27.给出每个员工每年薪水涨幅超过5000的员工编号emp_no、薪水变更开始日期from_date以及薪水涨幅值salary_growth,并按照salary_growth逆序排列。提示:在sqlite中获取datetime时间对应的年份函数为strftime(‘%Y’, to_date)
1
2
3
4
5
6
7
8
9
10
SELECT sa1.emp_no, sa2.from_date, (sa2.salary - sa1.salary) AS salary_growth
FROM salaries AS sa1 INNER JOIN salaries AS sa2 ON sa1.emp_no = sa2.emp_no
WHERE sa2.salary-sa1.salary>5000
AND (
strftime('%Y' , sa2.to_date) - strftime('%Y' , sa1.to_date) = 1
OR
strftime('%Y' , sa2.from_date) - strftime('%Y' , sa1.from_date) = 1
)
ORDER BY salary_growth DESC ;
28.查找描述信息中包括robot的电影对应的分类名称以及电影数目,而且还需要该分类对应电影数量>=5部
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT c.name,COUNT (fc.film_id)
FROM (
SELECT category_id
FROM film_category
GROUP BY category_id
HAVING COUNT (film_id) >= 5
) AS cc,
film AS f,
category AS c,
film_category AS fc
WHERE f.description LIKE '%robot%'
AND c.category_id = fc.category_id
AND c.category_id = cc.category_id
AND fc.film_id = f.film_id;
29.使用join查询方式找出没有分类的电影id以及名称 方法一
1
2
3
4
SELECT f.film_id, f.title
FROM film AS f LEFT JOIN film_category AS fc
ON f.film_id = fc.film_id
WHERE fc.category_id IS NULL ;
方法二1
2
3
4
5
6
SELECT film.film_id, film.title
FROM film
WHERE film.film_id NOT IN (
SELECT film_id
FROM film_category
);
30.使用子查询的方式找出属于Action分类的所有电影对应的title,description
子查询方法:
1
2
3
4
5
6
7
8
9
10
11
SELECT title, description
FROM film
WHERE film_id IN (
SELECT film_id
FROM film_category
WHERE category_id IN (
SELECT category_id
FROM category
WHERE name = 'Action'
)
);
非子查询方法
1
2
3
4
SELECT f.title, f.description
FROM film AS f INNER JOIN film_category AS fc ON f.film_id =fc.film_id
INNER JOIN category AS c ON c.category_id = fc.category_id
WHERE c.name = 'Action' ;
31.获取select * from employees对应的执行计划
1
EXPLAIN SELECT * FROM employees;
32.将employees表的所有员工的last_name和first_name拼接起来作为Name,中间以一个空格区分
1
2
3
4
5
6
7
// mysql写法
SELECT concat (concat (last_name, " " ), first_name)
FROM employees;
//sqlite写法
SELECT last_name||" " ||first_name AS Name
FROM employees;
33.创建一个actor表,包含如下列信息
列表 类型 是否为NULL 含义 actor_id smallint(5) not null 主键id first_name varchar(45) not null 名字 last_name varchar(45) not null 姓氏 last_update timestamp not null 最后更新时间,默认是系统的当前时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//sqlite写法
CREATE TABLE IF NOT EXISTS actor(
actor_id smallint (5 ) NOT NULL ,
first_name varchar (45 ) NOT NULL ,
last_name varchar (45 ) NOT NULL ,
last_update timestamp NOT NULL DEFAULT (datetime('now' , 'localtime' )),
PRIMARY KEY (actor_id)
);
//mysql写法
CREATE TABLE IF NOT EXISTS actor(
actor_id smallint (5 ) NOT NULL ,
first_name varchar (45 ) NOT NULL ,
last_name varchar (45 ) NOT NULL ,
last_update timestamp NOT NULL DEFAULT current_timestamp COMMENT '最后更新时间,默认是系统的当前时间' ,
PRIMARY KEY (actor_id)
);
34.对于表actor批量插入如下数据 actor_id first_name last_name last_update 1 PENELOPE GUINESS 2006-02-15 12:34:33 2 NICK WAHLBERG 2006-02-15 12:34:33
注意插入字符串时用引号1
2
3
4
5
6
7
INSERT INTO actor VALUES (1 , 'PENELOPE' , 'GUINESS' , '2006-02-15 12:34:33' ),
(2 , 'NICK' , 'WAHLBERG' , '2006-02-15 12:34:33' );
//或者
INSERT INTO actor(actor_id, first_name, last_name, last_update)
VALUES (1 , 'PENELOPE' , 'GUINESS' , '2006-02-15 12:34:33' ),
(2 , 'NICK' , 'WAHLBERG' , '2006-02-15 12:34:33' );
35. 对于表actor批量插入如下数据,如果数据已经存在,请忽略,不使用replace操作
1
2
3
4
5
//sqlite
INSERT OR IGNORE INTO actor VALUES (3 , 'ED' , 'CHASE' , '2006-02-15 12:34:33' );
//mysql
INSERT IGNORE INTO actor VALUES (3 , 'ED' , 'CHASE' , '2006-02-15 12:34:33' );
36.对于如下表actor,其对应的数据为: actor_id first_name last_name last_update 1 PENELOPE GUINESS 2006-02-15 12:34:33 2 NICK WAHLBERG 2006-02-15 12:34:33
创建一个actor_name表,将actor表中的所有first_name以及last_name导入改表。 actor_name表结构如下: 列表 类型 是否为NULL 含义 first_name varchar(45) not null 名字 last_name varchar(45) not null 姓氏
1
2
3
4
5
6
7
8
9
CREATE TABLE IF NOT EXISTS actor_name(
first_name varchar (45 ) NOT NULL ,
last_name varchar (45 ) NOT NULL
);
INSERT INTO actor_name (first_name, last_name) SELECT first_name, last_name FROM actor;
//或者
CREATE TABLE actor_name as SELECT first_name, last_name FROM actor;
37. 对first_name创建唯一索引uniq_idx_firstname,对last_name创建普通索引idx_lastname
1
2
CREATE UNIQUE INDEX uniq_idx_firstname ON actor(first_name);
CREATE INDEX idx_lastname ON actor(last_name);
38.针对actor表创建视图actor_name_view,只包含first_name以及last_name两列,并对这两列重新命名,first_name为first_name_v,last_name修改为last_name_v
1
2
CREATE VIEW actor_name_view (first_name_v, last_name_v) AS
SELECT first_name,last_name FROM actor
39.针对salaries表emp_no字段创建索引idx_emp_no,查询emp_no为10005, 使用强制索引。
1
2
3
4
5
//SQLite
SELECT * FROM salaries INDEXED BY idx_emp_no WHERE emp_no = 10005 ;
//MySQL
SELECT * FROM salaries FORCE INDEX idx_emp_no WHERE emp_no = 10005 ;
40. 现在在last_update后面新增加一列名字为create_date, 类型为datetime, NOT NULL,默认值为’0000 00:00:00’
1
ALTER TABLE actor ADD COLUMN create_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00' ;
41. 构造一个触发器audit_log,在向employees_test表中插入一条数据的时候,触发插入相关的数据到audit中。
1
2
3
4
CREATE TRIGGER audit_log AFTER INSERT ON employees_test
BEGIN
INSERT INTO audit VALUES (NEW.ID, NEW.NAME);
END ;
42. 删除emp_no重复的记录,只保留最小的id对应的记录。 思路:先把emp_no分组,在每组中找出最小的id,然后把非最小的都给删掉
1
2
3
DELETE FROM titles_test WHERE id NOT IN (
SELECT MIN (id ) FROM titles_test GROUP BY emp_no
);
43. 将所有to_date为9999-01-01的全部更新为NULL,且 from_date更新为2001-01-01。
1
2
UPDATE titles_test SET to_date = NULL , from_date = '2001-01-01'
WHERE to_date ='9999-01-01' ;
44. 将id=5以及emp_no=10001的行数据替换成id=5以及emp_no=10005,其他数据保持不变,使用replace实现。 1
REPLACE INTO titles_test VALUES ('5' , '10005' , 'Senior Engineer' , '1986-06-26' , '9999-01-01' );
45. 将titles_test表名修改为titles_2017。 1
ALTER TABLE titles_test RENAME TO titles_2017;
46.在audit表上创建外键约束,其emp_no对应employees_test表的主键id。 1
2
3
4
5
6
7
8
9
//mysql
ALTER TABLE audit ADD FROEIGN KEY (emp_no) REFERNCES employees_test (id );
//通过测试
DROP TABLE audit ;
CREATE TABLE audit (
EMP_no INT NOT NULL ,
create_date datetime NOT NULL ,
FOREIGN KEY (EMP_no) REFERENCES employees_test(ID ));
47.存在如下的视图:create view emp_v as select * from employees where emp_no >10005;如何获取emp_v和employees有相同的数据?
INETRSECT和 UNION 指令类似,INTERSECT 也是对两个 SQL 语句所产生的结果做处理的。不同的地方是, UNION 基本上是一个 OR (如果这个值存在于第一句或是第二句,它就会被选出),而 INTERSECT 则比较像 AND ( 这个值要存在于第一句和第二句才会被选出)。UNION 是联集,而 INTERSECT 是交集。
1
SELECT * FROM employees INTERSECT SELECT * FROM emp_v;
48. 将所有获取奖金的员工当前的薪水增加10%。 1
2
3
4
UPDATE salaries SET salary = salary * 1.1
WHERE to_date ='9999-01-01' AND emp_no IN (
SELECT emp_no FROM emp_bonus
);
49. 针对库中的所有表生成select count(*)对应的SQL语句
1
2
3
4
5
6
7
8
9
10
11
//sqlite
SELECT "select count(*) from " || name || ";" AS cnts
FROM sqlite_master WHERE type = 'table' ;
//mysql
SELECT concat ('SELECT COUNT(*) FROM ' , new.table_name, ';' ) AS cnts
FROM (
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'sql_nowcoder'
) AS new ;
50.将employees表中的所有员工的last_name和first_name通过(‘)连接起来。 1
2
3
4
5
6
7
//sqlite
SELECT last_name || "'" || first_name
FROM employees;
//mysql
SELECT concat (last_name, "'" , first_name)
FROM employees;
51.查找字符串’10,A,B’ 中逗号’,’出现的次数cnt。
1
2
3
SELECT (
(length ("10,A,B" ) - length (replace ("10,A,B" , "," , "" ))) / length ("," )
) AS cnt;
52.获取Employees中的first_name,查询按照first_name最后两个字母,按照升序进行排列
substr(string,start,length)
string 指定的要截取的字符串。
start 必需,规定在字符串的何处开始。正数 - 在字符串的指定位置开始,负数 - 在从字符串结尾的指定位置开始,0 - 在字符串中的第一个字符处开始。
length 可选,指定要截取的字符串长度,缺省时返回字符表达式的值结束前的全部字符。1
2
3
4
5
6
7
8
9
//sqlite
SELECT first_name
FROM employees
ORDER BY substr (first_name, length (first_name)-1 , 2 ) ASC ;
//mysql
SELECT first_name
FROM employees
ORDER BY RIGHT (first_name,2 ) ASC ;
53.按照dept_no进行汇总,属于同一个部门的emp_no按照逗号进行连接,结果给出dept_no以及连接出的结果employees
思路: 聚合函数group_concat(X,Y),其中X是要连接的字段,Y是连接时用的符号,可省略,默认为逗号。此函数必须与 GROUP BY 配合使用。此题以 dept_no 作为分组,将每个分组中不同的emp_no用逗号连接起来(即可省略Y)。
1
2
3
4
SELECT dept_no, group_concat (emp_no) AS employees
FROM dept_emp
GROUP BY dept_no;
54.查找排除当前最大、最小salary之后的员工的平均工资avg_salary。
1
2
3
4
5
SELECT AVG (salary) AS avg_salary
FROM salaries
WHERE to_date ='9999-01-01'
AND salary NOT IN (SELECT MAX (salary) FROM salaries WHERE to_date ='9999-01-01' )
AND salary NOT IN (SELECT MIN (salary) FROM salaries WHERE to_date ='9999-01-01' );
55.分页查询employees表,每5行一页,返回第2页的数据
思路:第2页的行数为第6-10行,可以用LIMIT 5, OFFSET 5
1
SELECT * FROM employees LIMIT 5 OFFSET 5 ;
56.获取所有员工的emp_no、部门编号dept_no以及对应的bonus类型btype和received ,没有分配具体的员工不显示
1
2
3
4
5
SELECT em.emp_no, de.dept_no, eb.btype, eb.recevied
FROM employees AS em INNER JOIN dept_emp AS de
ON em.emp_no = de.emp_no
LEFT JOIN emp_bonus AS eb
ON de.emp_no = eb.emp_no;
57.获取employees中的行数据,且这些行也存在于emp_v中。注意不能使用intersect关键字
58.获取有奖金的员工相关信息。给出emp_no、first_name、last_name、奖金类型btype、对应的当前薪水情况salary以及奖金金额bonus。 bonus类型btype为1其奖金为薪水salary的10%,btype为2其奖金为薪水的20%,其他类型均为薪水的30%。 当前薪水表示to_date=’9999-01-01’
1
2
3
4
5
6
7
8
SELECT emp.emp_no, emp.first_name, emp.last_name, eb.btype, sa.salary,
(CASE eb.btype
WHEN 1 THEN sa.salary*0.1
WHEN 2 THEN sa.salary*0.2
ELSE sa.salary*0.3
END ) AS bonus
FROM employees AS emp INNER JOIN emp_bonus AS eb ON emp.emp_no = eb.emp_no
INNER JOIN salaries AS sa ON emp.emp_no = sa.emp_no AND sa.to_date='9999-01-01' ;
59. 按照salary的累计和running_total,其中running_total为前两个员工的salary累计和,其他以此类推。 *
1
2
3
4
5
6
7
8
9
10
11
12
//窗口函数方法
SELECT emp_no, salary,
SUM (salary) OVER (order by emp_no) AS running_total
FROM salaries
WHERE to_date = '9999-01-01' ;
//联结表写法
SELECT sa2.emp_no, sa2.salary, SUM (sa1.salary) AS running_total
FROM salaries AS sa1 INNER JOIN salaries AS sa2
ON sa1.emp_no <= sa2.emp_no
WHERE sa1.to_date = "9999-01-01" AND sa2.to_date = "9999-01-01"
GROUP BY sa2.emp_no;
60. 对于employees表中,给出奇数行的first_name
思路:有多少个大于等于e2.first_name的记录的个数就是e2.first_name的行号,比如:
如果 e1.first_name 是第一位,那 e2.first_name 只有1个,就是 e1.first_name 本身,1%2=1; 如果 e1.first_name 排在第二位,就有它和比它小的这2个e2.first_name,2%2=0,所以不选, 以此类推。
1
2
3
4
5
SELECT e1.first_name FROM employees e1
WHERE (
SELECT count (*) FROM employees e2
WHERE e1.first_name >= e2.first_name
) % 2 = 1 ;