3. 人力资源数据(二)(SQL39~SQL61)

SQL39 针对上面的salaries表emp_no字段创建索引idx_emp_no,查询emp_no为10005
思路:考察用 FORCE INDEX 语句强制使用索引

SELECT *
FROM salaries
FORCE INDEX(idx_emp_no)
-- 注意位置:FROM之后,WHERE之前
WHERE emp_no = 10005;

注意:题干表述有误,听上去好像要自己先创建索引一样……实际是要你强制使用已经存在的索引

SQL40 在last_update后面新增加一列名字为create_date
存在actor表,现在在last_update后面新增加一列名字为create_date, 类型为datetime, NOT NULL,默认值为'2020-10-01 00:00:00'

ALTER TABLE actor
    ADD create_date 
        DATETIME NOT NULL 
        DEFAULT '2020-10-01 00:00:00' 
        AFTER last_update;

SQL41 构造一个触发器audit_log,在向employees表中插入一条数据的时候,触发插入相关的数据到audit中

CREATE TRIGGER audit_log
    AFTER INSERT ON employees_test
    FOR EACH ROW
BEGIN
    INSERT INTO audit
        VALUES(NEW.id, NEW.name);
END;

SQL42 删除emp_no重复的记录,只保留最小的id对应的记录
mysql 中不允许在子查询的同时删除表数据(不能一边查一边把查的表删了),而 sqllite 允许这样做;mysql 中可以再嵌套一层查询来解决:

DELETE FROM titles_test
WHERE id NOT IN(
    SELECT * 
    FROM(
        SELECT MIN(id)
        FROM titles_test
        GROUP BY emp_no
    ) a  -- 把得出的表重命名那就不是原表了(机智.jpg
);

小结:关键是如何将题目的含义 转化 为 SQL 能听懂的含义,比如这个题就是:将 "删除emp_no重复的记录,只保留最小的id对应的记录" 转化为 "删除掉那些不是 【相同emp_no中的最小id】的那些记录",这样就能通过 NOT IN + 子查询来解决
SQL43 将所有to_date为9999-01-01的全部更新为NULL,且from_date更新为2001-01-01

UPDATE titles_test
SET 
    to_date = NULL,
    from_date = '2001-01-01'
WHERE to_date = '9999-01-01';

SQL44 将id=5以及emp_no=10001的行数据替换成id=5以及emp_no=10005,其他数据保持不变,使用replace实现
注意 REPLACE 和 INSERT INTO 一样是整行插入或整行替换,而题目只想替换 id 和 emp_no 两个字段,其他字段保持不变,一种方法:
https://www.yiibai.com/mysql/replace.html

REPLACE INTO titles_test
VALUES ('5', '10005', 'Senior Engineer', '1986-06-26', '9999-01-01');

但这种方法下,其他字段的值也必须清晰地写出来,还有一种方法:

REPLACE INTO titles_test
SELECT 5, 10005, 【title, from_date, to_date】
FROM titles_test
WHERE id = 5;

通过这种 SELECT + REPLACE INTO 的方法,后面三个保持不变的字段不用把具体的值写出来
SQL45 将titles_test表名修改为titles_2017
https://www.yiibai.com/mysql/rename-table.html
RENAME TABLE语句不能用于重命名临时表,可以使用ALTER TABLE语句来重命名一个临时表。

-- 法1:
RENAME TABLE titles_test TO titles_2017;

-- 法2:
ALTER TABLE titles_test
RENAME TO titles_2017;

SQL46 在audit表上创建外键约束,其emp_no对应employees_test表的主键id

ALTER TABLE audit 
    ADD FOREIGN KEY fk_audit_employees_test (emp_no)
    REFERENCES employees_test (id)
        ON UPDATE CASCADE
        ON DELETE NO ACTION;

SQL48 将所有获取奖金的员工当前的薪水增加10%
写出更新语句,将所有获取奖金的员工当前的(salaries.to_date='9999-01-01')薪水增加10%。(emp_bonus里面的emp_no都是当前获奖的所有员工)
法一:连接

UPDATE salaries AS s 
JOIN emp_bonus AS e ON s.emp_no=e.emp_no
SET salary=salary*1.1
WHERE to_date='9999-01-01'

法二:子查询

UPDATE salaries
SET salary=salary*1.1
WHERE to_date='9999-01-01'
AND emp_no IN(select emp_no FROM emp_bonus);

SQL50 将employees表中的所有员工的last_name和first_name通过(')连接起来。
主要考查用转义字符 —— 反斜线 \ 或用双引号"'"

SELECT CONCAT(last_name, '\'', first_name)
FROM employees;

SQL51 查找字符串'10,A,B' 中逗号','出现的次数cnt。

SELECT
    LENGTH('10,A,B') - LENGTH(REPLACE('10,A,B', ',', '')) AS cnt;

REPLACE(String,from_str,to_str) 即:将String中所有出现的from_str替换为to_str。

SQL52 获取Employees中的first_name,查询按照first_name最后两个字母,按照升序进行排列

SELECT first_name
FROM employees
ORDER BY RIGHT(first_name, 2);

SQL53 按照dept_no进行汇总,属于同一个部门的emp_no按照逗号进行连接,结果给出dept_no以及连接出的结果employees

SELECT 
    dept_no,
    GROUP_CONCAT(emp_no) employees
FROM dept_emp
GROUP BY dept_no;

SQL54 查找排除最大、最小salary之后的当前(to_date = '9999-01-01' )员工的平均工资avg_salary
注意:排除的最大最小工资也要是当前的(to_date = '9999-01-01' )

SELECT AVG(salary) avg_salary
FROM salaries
WHERE salary NOT IN (
    SELECT MIN(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 to_date = '9999-01-01'

SQL55 分页查询employees表,每5行一页,返回第2页的数据

SELECT *
FROM employees
LIMIT 5,5

limit(x,y) 从第x条记录(不包含x)后开始,返回y条数据

SQL57 使用含有关键字exists查找未分配具体部门的员工的所有信息。
EXISTS() 和 ISNULL() 很类似,只是后者只能判断一个单元格的数据是否为空,前者是判断一个结果(可能是一个单元格、一个列、一个表)是否为空,即前者能判断的范围更广。结果为布林值 TRUE/FALSE,自然也就可以在前面加 NOT 来取反
法1. NOT EXISTS + 关联子查询:

SELECT *
FROM employees e
WHERE NOT EXISTS(
    SELECT emp_no 
    FROM dept_emp 
    WHERE emp_no = e.emp_no
);

如果不要求用 EXISTS 关键字的话,还有两种更常用的方法

法2. NOT IN + 简单子查询实现:

SELECT *
FROM employees e
WHERE emp_no NOT IN(
    SELECT emp_no 
    FROM dept_emp 
);

法3. LEFT JOIN + IS NULL 实现:

SELECT e.*
FROM employees e
LEFT JOIN dept_emp d USING(emp_no)
WHERE d.dept_no IS NULL;

SQL59 获取有奖金的员工相关信息。
获取有奖金的员工相关信息。给出emp_no、first_name、last_name、奖金类型btype、对应的当前薪水情况salary以及奖金金额bonus。 bonus类型btype为1其奖金为薪水salary的10%,btype为2其奖金为薪水的20%,其他类型均为薪水的30%。 当前薪水表示to_date='9999-01-01'

SELECT
    e.emp_no,
    first_name,
    last_name,
    btype,
    salary,
    CASE
        WHEN btype = 1 THEN salary * 0.1
        WHEN btype = 2 THEN salary * 0.2
        ELSE salary * 0.3
    END AS bonus
FROM employees e
JOIN emp_bonus eb USING(emp_no)
JOIN salaries s 
    ON e.emp_no = s.emp_no
    AND s.to_date = '9999-01-01';

SQL60 统计salary的累计和running_total
按照salary的累计和running_total,其中running_total为前N个当前( to_date = '9999-01-01')员工的salary累计和,其他以此类推。
法1. 窗口函数

SELECT
    emp_no,
    salary,
    SUM(salary) OVER(ORDER BY emp_no) AS running_total
FROM salaries
WHERE to_date = '9999-01-01'
ORDER BY emp_no;

法2. 子查询

MySQL 8.0 之后才能使用窗口函数,如果是 5.7 及以下版本的话,就要用子查询法,所以两种方法都要掌握

SELECT
    emp_no,
    salary,
    (SELECT SUM(salary) 
    FROM salaries
    WHERE emp_no <= s.emp_no
    【AND to_date = '9999-01-01'】) AS running_total
    /* 别忘了子查询内部也要有对to_date的筛选条件
    (子查询和外层查询是两个查询,WHERE筛选条件各论各的)*/
FROM salaries s
WHERE to_date = '9999-01-01'
ORDER BY emp_no;

SQL61 对于employees表中,输出first_name排名(按first_name升序排序)为奇数的first_name,且输出时不需排序
法1. 窗口函数法

注意:从题干其实看不出相同 first_name 名次是否要并列,所以不知道要用 ROW_NUMBER() 还是 RANK() ,实验发现两种均可,应该是不考虑 first_name 相同的问题
说的是不用排序,但只有按 emp_no 排序才能通过

SELECT first_name
FROM(
    SELECT 
        emp_no,
        first_name,
        RANK() OVER(ORDER BY first_name) AS rk
    FROM employees
) AS a
WHERE rk % 2 = 1
ORDER BY emp_no;

法2. 子查询法

SELECT first_name
FROM employees e
WHERE (
    SELECT COUNT(*) 
    FROM employees
    WHERE first_name <= e.first_name
    /* 
    如果 first_name 里可能用空值,这里还要加上一句:
    OR e.first_name IS NULL AND first_name IS NOT NULL
    但 first_name 里应该没有空值
    */
) % 2 = 1;
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务