SQL练习

 

 

175. 组合两个表-easy

考察表的 join操作

select 
    FirstName, 
    LastName, 
    City, 
    State
from 
    Person 
left join 
    Address
on Person.PersonId = Address.PersonId

176. 第二高的薪水-easy

考察distinct/ order by/ limit / offset/ IFNULL

select 
    IFNULL(
        (select distinct Salary from Employee order by Salary DESC limit 1 offset 1)
        ,
        NULL
    )
as SecondHighestSalary

177. 第N高的薪水-medium

考察SQL里写带参数的函数function,用group by或者distinct去重。和176一致,只是增加了个参数。

这种解法形式最为简洁直观,但仅适用于查询全局排名问题,如果要求各分组的每个第N名,则该方法不适用。

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
  set N := N-1;
  RETURN (
      # Write your MySQL query statement below.
    select distinct Salary 
    from Employee 
    order by Salary DESC
    limit 1 offset N
  );
END

178. 分数排名-medium

用2张表,对表1中的每个数据都遍历表2找出比之大的数据条数作为rank,考察两表的遍历和count

select 
    a.Score as Score,
    (select count(distinct b.Score) from Scores b where b.Score >= a.Score) as Rank
from Scores a
order by a.Score DESC

180. 连续出现的数字-medium

连续出现的意味着相同数字的 Id 是连着的,用个表找3个连号的数据是否相等

select 
    distinct l1.Num as ConsecutiveNums 
from Logs l1, Logs l2, Logs l3
where l1.Id = l2.Id - 1 and l2.Id = l3.Id - 1 
    and l1.Num = l2.Num and l2.Num = l3.Num

601. 体育馆的人流量-hard

找出表中连续3天流量字段不低于100的记录,选中3个表没有join条件得到笛卡尔积,选三者同时流量超过100的表项,然后选是连续3天的表项,然后去重

SELECT 
    DISTINCT s1.*
FROM stadium s1, stadium s2, stadium s3
WHERE s1.people >=100 AND s2.people >=100 AND s3.people >=100
    AND
    (
        (s2.id - s1.id = 1 AND s3.id - s2.id = 1 AND s3.id - s1.id = 2)
        OR
        (s1.id - s2.id = 1 AND s3.id - s1.id = 1 AND s3.id - s2.id = 2)
        OR
        (s1.id - s2.id = 1 AND s2.id - s3.id = 1 AND s1.id - s3.id = 2)
    )
ORDER BY s1.id;

181. 超过经理收入的员工-easy

需要两张表,一张表取id另一张表取manager id,可以直接取也可以拼接在一起。考察where

select 
    a.Name as Employee 
from
    Employee a, Employee b
where a.ManagerId = b.Id and a.Salary > b.Salary

# 用join,貌似效率更低
select 
    a.Name as Employee 
from
    Employee a join Employee b
where a.ManagerId = b.Id and a.Salary > b.Salary

182. 查找重复的电子邮箱-easy

使用group by然后用count计数,构造有这两个特征的临时表,然后用where选取符合条件的。

以上的语句可以用having得到简化。我理解having一般是跟在group by后面,where是筛选数据表中样本,having是筛选分组之后的符合条件的组

在 SQL 中增加 HAVING 子句原因是,WHERE 关键字无法与合计函数一起使用。

当同时含有where子句、group by 子句 、having子句及聚集函数时,执行顺序如下:

  • 执行where子句查找符合条件的数据;
  • 使用group by 子句对数据进行分组;对group by 子句形成的组运行聚集函数计算每一组的值;
  • 最后用having 子句去掉不符合条件的组。

select dno,count(*)
from employee
group by dno
having count(*)>3

select Email from 
    (select Email, count(Email) as num from Person group by Email) statistic
where num > 1

# 简化后
select 
    Email 
from Person 
group by Email 
having count(Email) > 1

196. 删除重复的电子邮箱-easy

选2个表,可以是直接取也可以join,然后选取id更小的

DELETE p1 
FROM
    Person p1, Person p2
WHERE
    p1.Email = p2.Email AND p1.Id > p2.Id

# join
DELETE p1 
FROM
    Person p1 JOIN Person p2 
        ON p1.Email = p2.Email 
        WHERE p1.Id > p2.Id

 

183. 从不订购的客户-easy

考察not in的用法。

也可以用A left join B,以A为基准,B 中join不到的会呈现为NULL,这部分就是我们要的结果,只在表A不在表B的样本

select 
    Name as Customers 
from Customers 
where Id not in (select CustomerId from Orders)

# left join
select 
    Name as Customers 
from Customers 
left join 
    Orders 
on Customers.Id = Orders.CustomerId 
where Orders.CustomerId is Null

184. 部门工资最高的员工-medium

先选出符合结果format的表项,然后用where筛选,in的判断可以用多元组

select 
    Department.Name as Department, 
    Employee.Name as Employee, 
    Employee.Salary as Salary 
from 
    Employee 
        join 
    Department
        on 
    Employee.DepartmentId = Department.Id
where 
    (Employee.DepartmentId, Salary) IN
    (
        select 
            DepartmentId, MAX(Salary)
        from
            Employee
        GROUP BY DepartmentId
    )

185. 部门工资前三高的所有员工-hard

解决思路有点类似 181. 超过经理收入的员工,就是对选出来的表项k要满足:同部门的所有表项salary比它大的数量要小于3

SELECT 
    Department.Name AS Department,
    e1.Name AS Employee,
    e1.Salary
From
    Employee e1
        JOIN
    Department
        ON
    e1.DepartmentId = Department.Id
WHERE
    3 
    >
    (SELECT 
        count(distinct e2.Salary)
    FROM 
        Employee e2
    WHERE e2.Salary > e1.Salary AND e2.DepartmentID = e1.DepartmentId # 限定是同一个部门的
    )

569. 工资中位数-hard 

bonus for not using any built-in function in SQL

SELECT
    id,
    company,
    salary
FROM 
    Employee e
WHERE 1 <= ABS(
                (SELECT COUNT(*) FROM Employee e1 WHERE e.salary >= e1.salary)
                -
                (SELECT COUNT(*) FROM Employee e2 WHERE e.salary <= e2.salary)
                )
GROUP BY company, salary

615. 工资平均值:每个部门的员工 VS 公司-hard

 求出每个月,各个部门的平均工资比公司的平均工资高低。先求每个部门的平均工资,再求公司的平均工资,再join到一张表中用case when判断高还是低

SELECT
    d1.pay_month,
    d1.department_id,
    CASE WHEN 
            d1.department_avg > c1.company_avg THEN 'higher'
         WHEN 
            d1.department_avg < c1.company_avg THEN 'lower'
         ELSE 'same'
    END AS 'comparison'
FROM
    (
        (SELECT 
            LEFT(s1.pay_date, 7) AS `pay_month`, 
            e1.department_id, AVG(s1.amount) AS `department_avg`
        FROM Salary s1
        JOIN
            Employee e1
        ON  
            s1.employee_id = e1.employee_id
        GROUP BY pay_month, e1.department_id     
        ) d1
    LEFT JOIN
        (
        SELECT 
            LEFT(pay_date, 7) AS `pay_month`,
            AVG(amount) AS `company_avg`
        FROM Salary
        GROUP BY pay_month
        ) c1
    ON
        d1.pay_month = c1.pay_month
    )
ORDER BY pay_month DESC, department_id;

570. Managers with at least 5 direct reporters-meidum

要输出manager的名字而非ID

SQL练习_第1张图片

SELECT
    `name`
FROM 
    Employee e1
JOIN
    (
        SELECT 
            manager_id
        FROM
            Employee e2
        GROUP BY manager_id
        HAVING COUNT(*) >= 5
    ) e2
ON e1.id = e2.manager_id

197. 上升的温度-easy

日期差一天用DATEDIFF函数,同样这道题有直接取和用join两种方法

SELECT
    w1.Id
FROM
    Weather w1, Weather w2 
    WHERE DATEDIFF(w1.RecordDate, w2.RecordDate) = 1
        AND w1.Temperature > w2.Temperature

# join
SELECT
    Weather.Id
FROM
    Weather 
        JOIN 
    Weather w 
        ON DATEDIFF(Weather.RecordDate, w.RecordDate) = 1
            AND Weather.Temperature > w.Temperature

262. 行程和用户-hard

group by后用聚合函数如sum之类的,注意Trips里司机和乘客共同构成一条数据,Users里司机和乘客ID是唯一的,是分开的,如何根据User里面的有效状态字段去筛选Trips里有效的样本,第一次join选司机的有效样本,第二次join选乘客的有效样本

SELECT 
    t.Request_at as Day,
    ROUND(
        SUM(
            IF(t.STATUS = 'completed', 0, 1)
        )
        /
        COUNT(t.STATUS),
        2
    ) AS `Cancellation Rate`
FROM Trips t
JOIN 
    Users u1 ON (t.client_id = u1.users_id AND u1.banned = 'No')
JOIN 
    Users u2 ON (t.client_id = u2.users_id AND u2.banned = 'No')
WHERE t.request_at BETWEEN '2013-10-01' AND '2013-10-03'
GROUP BY t.request_at

620. 有趣的电影-easy

考点判断id是奇数,用mod函数

SELECT 
    *
FROM cinema
WHERE description != 'boring' AND MOD(id, 2) = 1
ORDER BY rating DESC

626. 换座位-medium

改变表,相邻两个换座位,两两换,不是连续换。用if或者case when,比较麻烦的是要先算出来id总数

SELECT
    IF(id%2=0, id-1,
        IF(
            id = (SELECT COUNT(DISTINCT id) FROM seat s),
            id,
            id+1
        )
    ) AS id,
    student
FROM seat
ORDER BY id;

627. 交换工资-easy

考察的不是查询,是CRUD里的update

UPDATE salary
SET
    sex = CASE sex
            WHEN 'm' THEN 'f'
            ELSE 'm'
          END; # 这个end对应的是case when

# 简洁写法
UPDATE salary
SET
    sex = CHAR(ASCII('m') + ASCII('f') - ASCII(sex));

597. Friend requests I: overall acceptance rate-easy

2个表,一个send请求,一个接受请求,求接受率 = 接受数/发送数,注意同一个请求发送-接受多次只算一次

SELECT
    IFNULL(
        ROUND(
            COUNT(DISTINCT requester_id, accepter_id)
            /
            COUNT(DISTINCT sender_id, sender_to_id)
            ,
            2
        )
        ,
        0
    ) AS accept_rate
FROM
request_accepted, friend_request

 

你可能感兴趣的:(Database,sql,数据库)