文章目录
[高频 SQL 50 题(基础版)](https://leetcode.cn/studyplan/sql-free-50/)
1 查询
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| product_id | int |
| low_fats | enum |
| recyclable | enum |
+-------------+---------+
product_id 是该表的主键(具有唯一值的列)。
low_fats 是枚举类型,取值为以下两种 ('Y', 'N'),其中 'Y' 表示该产品是低脂产品,'N' 表示不是低脂产品。
recyclable 是枚举类型,取值为以下两种 ('Y', 'N'),其中 'Y' 表示该产品可回收,而 'N' 表示不可回收。
select product_id from Products where low_fats="Y" and recyclable="Y";
输入:
Customer 表:
+----+------+------------+
| id | name | referee_id |
+----+------+------------+
| 1 | Will | null |
| 2 | Jane | null |
| 3 | Alex | 2 |
| 4 | Bill | null |
| 5 | Zack | 1 |
| 6 | Mark | 2 |
+----+------+------------+
输出:
+------+
| name |
+------+
| Will |
| Jane |
| Bill |
| Zack |
+------+
select name from Customer
where referee_id!=2 or referee_id is null;
MySQL 使用三值逻辑 —— TRUE, FALSE 和 UNKNOWN。任何与 NULL 值进行的比较都会与第三种值 UNKNOWN 做比较。这个“任何值”包括 NULL 本身!这就是为什么 MySQL 提供 IS NULL 和 IS NOT NULL 两种操作来对 NULL 特殊判断。
因此,在 WHERE 语句中我们需要做一个额外的条件判断 `referee_id IS NULL’。
World 表:
±------------±--------+
| Column Name | Type |
±------------±--------+
| name | varchar |
| continent | varchar |
| area | int |
| population | int |
| gdp | bigint |
±------------±--------+
name 是该表的主键(具有唯一值的列)。
这张表的每一行提供:国家名称、所属大陆、面积、人口和 GDP 值。
如果一个国家满足下述两个条件之一,则认为该国是 大国 :
面积至少为 300 万平方公里(即,3000000 km2),或者
人口至少为 2500 万(即 25000000)
编写解决方案找出 大国 的国家名称、人口和面积。
按 任意顺序 返回结果表。
select name,population,area from World
where area>=3000000 or population>=25000000;
<font style="color:rgba(38, 38, 38, 0.75);background-color:#FFFFFF;">Views</font>
表:
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| article_id | int |
| author_id | int |
| viewer_id | int |
| view_date | date |
+---------------+---------+
此表可能会存在重复行。(换句话说,在 SQL 中这个表没有主键)
此表的每一行都表示某人在某天浏览了某位作者的某篇文章。
请注意,同一人的 author_id 和 viewer_id 是相同的。
请查询出所有浏览过自己文章的作者
结果按照 <font style="color:rgba(38, 38, 38, 0.75);background-color:#FFFFFF;">id</font>
升序排列。
查询结果的格式如下所示:
示例 1:
输入:
Views 表:
+------------+-----------+-----------+------------+
| article_id | author_id | viewer_id | view_date |
+------------+-----------+-----------+------------+
| 1 | 3 | 5 | 2019-08-01 |
| 1 | 3 | 6 | 2019-08-02 |
| 2 | 7 | 7 | 2019-08-01 |
| 2 | 7 | 6 | 2019-08-02 |
| 4 | 7 | 1 | 2019-07-22 |
| 3 | 4 | 4 | 2019-07-21 |
| 3 | 4 | 4 | 2019-07-21 |
+------------+-----------+-----------+------------+
输出:
+------+
| id |
+------+
| 4 |
| 7 |
+------+
select
distinct author_id as id
from Views
where author_id=viewer_id
order by id
表:<font style="color:rgba(38, 38, 38, 0.75);background-color:rgb(240, 240, 240);">Tweets</font>
+----------------+---------+
| Column Name | Type |
+----------------+---------+
| tweet_id | int |
| content | varchar |
+----------------+---------+
在 SQL 中,tweet_id 是这个表的主键。
这个表包含某社交媒体 App 中所有的推文。
查询所有无效推文的编号(ID)。当推文内容中的字符数严格大于 <font style="color:rgba(38, 38, 38, 0.75);background-color:#FFFFFF;">15</font>
时,该推文是无效的。
以任意顺序返回结果表。
查询结果格式如下所示:
输入:
Tweets 表:
+----------+----------------------------------+
| tweet_id | content |
+----------+----------------------------------+
| 1 | Vote for Biden |
| 2 | Let us make America great again! |
+----------+----------------------------------+
输出:
+----------+
| tweet_id |
+----------+
| 2 |
+----------+
解释:
推文 1 的长度 length = 14。该推文是有效的。
推文 2 的长度 length = 32。该推文是无效的。
select tweet_id
from Tweets
where char_length(content)>15;
对于SQL表,用于计算字符串中字符数的最佳函数是 CHAR_LENGTH(str),它返回字符串 str 的长度。
另一个常用的函数 LENGTH(str) 在这个问题中也适用,因为列 content 只包含英文字符,没有特殊字符。否则,LENGTH() 可能会返回不同的结果,因为该函数返回字符串 str 的字节数,某些字符包含多于 1 个字节。
以字符 ‘¥’ 为例:CHAR_LENGTH() 返回结果为 1,而 LENGTH() 返回结果为 2,因为该字符串包含 2 个字节。
2 连接
展示每位用户的 唯一标识码(unique ID );如果某位员工没有唯一标识码,使用 null 填充即可。
创建表并插入数据
首先,我们需要创建两个表 Employees 和 EmployeeUNI,并插入相应的数据。
-- 创建 Employees 表
CREATE TABLE Employees (
id INT PRIMARY KEY,
name VARCHAR(100)
);
-- 插入数据到 Employees 表
INSERT INTO Employees (id, name) VALUES
(1, 'Alice'),
(7, 'Bob'),
(11, 'Meir'),
(90, 'Winston'),
(3, 'Jonathan');
-- 创建 EmployeeUNI 表
CREATE TABLE EmployeeUNI (
id INT,
unique_id INT,
PRIMARY KEY (id, unique_id)
);
-- 插入数据到 EmployeeUNI 表
INSERT INTO EmployeeUNI (id, unique_id) VALUES
(3, 1),
(11, 2),
(90, 3);
执行查询
接下来,我们使用 LEFT JOIN 将这两个表连接起来,以获取每个员工的唯一标识码。如果某个员工没有唯一标识码,则 unique_id 字段会显示为 NULL。
select
u.unique_id,
e.name
from
Employees e
left join
EmployeeUNI u on e.id = u.id;
LEFT JOIN 确保了 Employees 表中的所有记录都包含在结果中。
如果某个员工在 EmployeeUNI 表中没有对应的记录,那么 unique_id 字段将显示为 NULL。
-- 创建 Sales 表
CREATE TABLE Sales (
sale_id INT,
product_id INT,
year INT,
quantity INT,
price INT,
PRIMARY KEY (sale_id, year)
);
-- 插入数据到 Sales 表
INSERT INTO Sales (sale_id, product_id, year, quantity, price) VALUES
(1, 100, 2008, 10, 5000),
(2, 100, 2009, 12, 5000),
(7, 200, 2011, 15, 9000);
-- 创建 Product 表
CREATE TABLE Product (
product_id INT PRIMARY KEY,
product_name VARCHAR(100)
);
-- 插入数据到 Product 表
INSERT INTO Product (product_id, product_name) VALUES
(100, 'Nokia'),
(200, 'Apple'),
(300, 'Samsung');
select
p.product_name,s.year,s.price
from
Sales s
left join
Product p
on
s.product_id=p.product_id
INNER JOIN:我们将 Sales 表和 Product 表通过 product_id 字段进行连接。这样可以确保只有当 Sales 表中的 product_id 在 Product 表中存在时,才会返回相应的记录。
选择字段:我们选择了 Product 表中的 product_name 和 Sales 表中的 year 和 price 字段。
-- 创建 Visits 表
CREATE TABLE Visits (
visit_id INT,
customer_id INT,
PRIMARY KEY (visit_id)
);
-- 插入数据到 Visits 表
INSERT INTO Visits (visit_id, customer_id) VALUES
(1, 23),
(2, 9),
(4, 30),
(5, 54),
(6, 96),
(7, 54),
(8, 54);
-- 创建 Transactions 表
CREATE TABLE Transactions (
transaction_id INT,
visit_id INT,
amount INT,
PRIMARY KEY (transaction_id)
);
-- 插入数据到 Transactions 表
INSERT INTO Transactions (transaction_id, visit_id, amount) VALUES
(2, 5, 310),
(3, 5, 300),
(9, 5, 200),
(12, 1, 910),
(13, 2, 970);
select
v.customer_id,
count(v.visit_id) as count_no_trans
from
Visits v
left join
Transactions t
on v.visit_id=t.visit_id
where
t.visit_id is null
group by
v.customer_id;
LEFT JOIN:将 Visits 表和 Transactions 表通过 visit_id 字段进行左连接。这样可以确保所有 Visits 表中的记录都会出现在结果中,即使它们在 Transactions 表中没有对应的记录。
WHERE t.visit_id IS NULL:这个条件用于筛选出那些在 Transactions 表中没有对应记录的访问记录,即那些没有进行交易的访问。
GROUP BY v.customer_id:我们按 customer_id 分组,以便统计每个顾客的无交易访问次数。
COUNT(v.visit_id):计算每个顾客的无交易访问次数。
找出那些温度比前一天高的日期,并返回这些日期对应的 <font style="color:rgb(44, 44, 54);">id</font>
。我们可以使用自连接(self join)来比较每一天的温度与其前一天的温度。
-- 创建 Weather 表
CREATE TABLE Weather (
id INT PRIMARY KEY,
recordDate DATE,
temperature INT
);
-- 插入数据到 Weather 表
INSERT INTO Weather (id, recordDate, temperature) VALUES
(1, '2015-01-01', 10),
(2, '2015-01-02', 25),
(3, '2015-01-03', 20),
(4, '2015-01-04', 30);
select w1.id
from
Weather w1
join
Weather w2
on w1.recordDate=date_add(w2.recordDate,interval 1 day)
where
w1.temperature > w2.temperature
- JOIN:我们将
<font style="color:rgb(44, 44, 54);background-color:#FAFAFA;">Weather</font>
表与自身进行连接,连接条件是<font style="color:rgb(44, 44, 54);background-color:#FAFAFA;">w1.recordDate</font>
等于<font style="color:rgb(44, 44, 54);background-color:#FAFAFA;">w2.recordDate</font>
的下一天(即<font style="color:rgb(44, 44, 54);background-color:#FAFAFA;">w1.recordDate = DATE_ADD(w2.recordDate, INTERVAL 1 DAY)</font>
)。这样可以确保我们在比较每一天的温度时,能够找到前一天的记录。 - WHERE w1.temperature > w2.temperature:这个条件用于筛选出那些温度比前一天高的记录。
- SELECT w1.id:我们只需要返回符合条件的记录的
<font style="color:rgb(44, 44, 54);background-color:#FAFAFA;">id</font>
。
- 计算每个进程的运行时间:对于每个进程,我们需要找到它的开始时间和结束时间,并计算它们之间的差值。
- 按机器分组:将计算出的运行时间按机器分组,计算每台机器的总运行时间和进程数量。
- 计算平均耗时:对于每台机器,计算总运行时间除以进程数量,并将结果四舍五入到三位小数。
-- 创建 Activity 表
CREATE TABLE Activity (
machine_id INT,
process_id INT,
activity_type ENUM('start', 'end'),
timestamp FLOAT,
PRIMARY KEY (machine_id, process_id, activity_type)
);
-- 插入数据到 Activity 表
INSERT INTO Activity (machine_id, process_id, activity_type, timestamp) VALUES
(0, 0, 'start', 0.712),
(0, 0, 'end', 1.520),
(0, 1, 'start', 3.140),
(0, 1, 'end', 4.120),
(1,