首页 > 数据库 >高频SQL 50题(基础版):连接

高频SQL 50题(基础版):连接

时间:2023-11-19 22:56:17浏览次数:46  
标签:JOIN name 50 Key SQL join 高频 id SELECT

SQL的各种join

1. INNER JOIN

内连接,将左表(表A)和右表(表B) 中能关联起来的数据连接后返回。

SELECT <select_list>
FROM TableA A
INNER JOIN TableB B
ON A.Key = B.Key

2. LEFT JOIN

左连接,也写作LEFT OUTER JOIN。这个连接会返回左表中的所有记录,不管右表中有没有关联的数据。在右表中找到的关联数据也会被一起返回。

SELECT <select_list>
FROM TableA A
LEFT JOIN TableB B
ON A.Key = B.Key

3. RIGHT JOIN

右连接,也写作RIGHT OUTER JOIN。这个连接返回右表中的所有记录,不管左表中有没有关联的数据。在左表中找到的关联数据列也会被一起返回。

SELECT <select_list>
FROM TableA A
RIGHT JOIN TableB B
ON A.Key = B.Key

4. FULL OUTER JOIN

外连接,也称为全连接,可以写作FULL OUTER JOIN或FULL JOIN。返回左右表中所有记录,左右表里能关联的记录被连接后返回。

SELECT <select_list>
FROM TableA A
FULL OUTER JOIN TableB B
ON A.Key = B.Key

5. LEFT JOIN EXCLUDING INNER JOIN

返回左表有但是右表没有关联数据的记录集。

SELECT <select_list>
FROM TableA A
FULL OUTER JOIN TableB B
ON A.Key = B.Key
WHERE B.Key IS NULL;

6. RIGHT JOIN EXCLUDING INNER JOIN

返回右表有但是左表没有关联数据的记录集。

SELECT <select_list>
FROM TableA A
RIGHT JOIN TableB B
ON A.Key = B.Key
WHERE A.Key IS NULL;

7. FULL OUTER JOIN EXCLUDING INNER JOIN

返回左表和右表中没有相互关联的记录集。

SELECT <select_list>
FROM TableA A
FULL OUTER JOIN TableB B
ON A.Key = B.Key
WHERE A.Key IS NULL
OR B.Key IS NULL;

让我们逐步解释这个查询:

  1. SELECT <select_list>: 这是一个占位符,代表你想要选择的列,用于指定查询结果中应包含哪些列。

  2. FROM TableA A: 这指定了查询的主要表是 TableA,将其别名为 A,以便在查询中引用它。

  3. FULL OUTER JOIN TableB B ON A.Key = B.Key: 这是一个外连接(FULL OUTER JOIN),它返回两个表中的所有行,以及两个表中匹配键的行。连接条件是 A.Key = B.Key,表示两个表中的 Key 列的值相等。

  4. WHERE A.Key IS NULL OR B.Key IS NULL: 这是一个筛选条件,用于选择在连接条件中没有匹配项的记录。具体来说,它选择那些在 TableA 中的 Key 列为NULL或在 TableB 中的 Key 列为NULL的记录。这是因为在 FULL OUTER JOIN 中,如果在其中一个表中找不到匹配项,那么相应的列将为NULL。

综合起来,这个查询的结果将包括那些在 TableA 中的 Key 列在 TableB 中没有匹配项的记录,以及在 TableB 中的 Key 列在 TableA 中没有匹配项的记录。

 

1378. 使用唯一标识码替换员工ID

思路:返回左表中所有记录,以及与右表有关联的记录,没有匹配的用null填充,注意返回结果的列需要通过select选择。

SELECT EmployeeUNI.unique_id, Employees.name
FROM Employees
LEFT JOIN EmployeeUNI
ON Employees.id = EmployeeUNI.id;

 

1068. 产品销售分析 I

SELECT Product.product_name, Sales.year, Sales.price
FROM Sales
LEFT JOIN Product
ON Sales.product_id = Product.product_id

 

1581. 进店却未进行过交易的顾客

思路:题目有点难理解,主要是统计交易次数。一种思路是利用visit_id进行左连接,则会出现一些id的transaction_id为空的情况,我们就是要找出为null的用户,然后计数customer_id出现的次数。

SELECT customer_id, count(customer_id) as count_no_trans
FROM Visits
LEFT JOIN Transactions
ON Visits.visit_id = Transactions.visit_id
WHERE transaction_id IS NULL
GROUP BY customer_id;

注意:

1. count和as指定计数列并指定别名;

2. GROUP BY分组后,count根据分组后的信息进行计算。

第二种思路是使用NOT IN,找出在Transactions表出现的不重复的visit_id,然后在Visits表去掉这些id,就找到了只访问不交易的id。

SELECT customer_id, count(visit_id) as count_no_trans
FROM Visits
WHERE visit_id NOT IN (SELECT DISTINCT visit_id FROM Transactions)
GROUP BY customer_id;

注意:

1. count和group by的应用,对于不交易的id分组计数,分组按照customer_id,计数是visit_id;

2. 子查询的应用,找出不在交易表中的id。

 

197. 上升的温度

思路:

第一种思路:lag()+datediff()

lag()和lead(0函数可以查询我们得到的结果集上下偏移相应行数的相应的结果。

lag()函数:查询当前行向上偏移n行对应的结果,函数有三个参数:第一个为待查询的参数列名,第二个为向上偏移的位数,第三个参数为超出最上面边界的默认值。

lead()函数:查询当前行向下偏移n行对应的结果,函数有三个参数:第一个为待查询的参数列名,第二个为向下偏移的位数,第三个参数为超出下面边界的默认值。

DATEDIFF()函数:返回两个日期之间的时间。

# Write your MySQL query statement below
SELECT 
    id
FROM 
    (SELECT
        id,
        temperature,
        recordDate,
        LAG(recordDate,1) OVER(ORDER BY recordDate) as last_date,
        LAG(temperature,1) OVER(ORDER BY recordDate) as last_temperature
    FROM
        weather) a
WHERE temperature > last_temperature and DATEDIFF(recordDate, last_date) = 1;

注意:

1. 窗口函数LAG和LEAD的使用,OVER确定窗口函数的操作范围,as确定别名。

2. 子查询需要给派生表指定一个a,如果不指定,则会报错。

3. 代码的逻辑实现,利用DATEDIFF日期函数计算两个日期之间的时间差。

第二种思路:笛卡尔积

笛卡尔积指的是数学中,两个集合X和Y的笛卡尔积,又称直积,表示为X×Y,第一个对象是X的成员而第二个对象是Y的所有可能有序对象的其中一个成员。

假设集合A={a, b},集合B={0, 1, 2},则两个集合的笛卡尔积为{(a,0), (a,1), (a,2), (b,0), (b, 1), (b, 2)}。

inner join只返回两个表中联结字段相等的行,on表示链接条件。

SELECT
    b.id
FROM
    weather a
INNER JOIN
    weather b
WHERE
    DATEDIFF(b.recordDate, a.recordDate)=1
   AND b.temperature > a.temperature;

具体步骤如下:

注意:MySQL只支持left join、right join和inner join,但是不支持full join。

第三种思路:adddate()函数

这个函数用于将日期与一个指定的时间间隔相加,返回一个新的日期。

SELECT
    a.id
FROM
    weather a
INNER JOIN
    weather b
ON 
    (a.recordDate = adddate(b.recordDate, INTERVAL 1 day))
WHERE
    a.temperature > b.temperature;

注意:自连接的话用inner join和join都行。adddate()和date_add()等价。

第四种思路:TIMESTAMPDIFF()

SELECT
    b.id
FROM
    weather a, weather b
WHERE
    TIMESTAMPDIFF(DAY, a.RecordDate, b.RecordDate) = 1
    AND b.temperature > a.temperature;

第五种思路:外连接+子查询+Date_ADD()

SELECT
    a.id
FROM
    weather a
JOIN(
    SELECT
        recordDate, temperature
    FROM
        weather
) b
ON
    a.recordDate = adddate(b.recordDate, INTERVAL 1 day)
WHERE
    a.temperature > b.temperature;

 

1661. 每台机器的进程的平均运行时间

 

SELECT
    a1.machine_id,
    ROUND(AVG(a2.timestamp - a1.timestamp), 3) AS processing_time
FROM
    Activity AS a1
    JOIN Activity AS a2 ON
        a1.machine_id = a2.machine_id
        AND a1.process_id = a2.process_id
        AND a1.activity_type = 'start'
        AND a2.activity_type = 'end'
GROUP BY
    machine_id;

注意:

1. round()函数,四舍五入,两个参数,第一个参数为要进行四舍五入的数字,第二个参数为要保留的小数位数。

2. avg()函数,参数为要计算平均值的列,经常和group by一起使用,group by对数据进行分组,avg计算每个组的聚合值。

 

577. 员工奖金

SELECT
    a1.name, a2.bonus
FROM
    employee a1
    LEFT JOIN bonus a2 ON
        a1.empId = a2.empId
WHERE a2.bonus < 1000 OR a2.bonus IS NULL;

 

1280. 学生们参加各科测试的次数

思路:

这个题目有点绕,做到最后提交发现没有考虑所有的科目,每个人都修了这些科目,但是exam表里只是参加过考试的科目。

首先我们通过一个子查询创建表grouped,统计每个学生参加每个科目的考试次数。

SELECT
    student_id, subject_name, COUNT(*) AS attened_exams
FROM
    Examinations
GROUP BY
    student_id, subject_name

group by先按照学生id进行分组,接着在每个学生id组内,根据科目名称进一步分组,具有相同科目名称的行被归为同一子组。count(*)计算的是每个组内的行数,每个组对应一个学生和一个科目的组合,返回考试的次数。

为了获得(subject_id, subject_name)的所有组合,我们使用交叉连接将表student中的每一行与表Subject中的每一行组合在一起,从而得到两个表中的student_id和subject_name的所有可能组合。

SELECT
    *
FROM
    Students s
CROSS JOIN
    Subjects sub

这一步之后,得到下边的表

然后将这个表与表grouped执行左连接,在左连接之后,attended_exams可能有null值,我们使用IFNULL()函数将其替换为0。

SELECT
    s.student_id, s.student_name, sub.subject_name, IFNULL(grouped.attended_exams, 0) AS attended_exams
FROM
    Students s
CROSS JOIN
    Subjects sub
LEFT JOIN (
    SELECT student_id, subject_name, COUNT(*) AS attended_exams
    FROM Examinations
    GROUP BY student_id, subject_name
) grouped
ON s.student_id = grouped.student_id AND sub.subject_name = grouped.subject_name
ORDER BY s.student_id, sub.subject_name;

注意:

1. 一步一步分析,分步创表,会使思路更加清晰;

2. 笛卡尔积可以通过cross join实现,实现两个表联合;

3. IFNULL函数检查一个表达式是否为NULL,如果是NULL就返回指定的替代值,否则返回原始值。

4. 注意表的命名,应该使其更容易理解。

 

570. 至少有5名直接下属的经理

第一种思路:

首先查询每个人的下属员工数,将两份Employee表用join连接,Manager表代表经理,Report表代表下属,每对Manager.Id = Report.ManagerId的情况代表此经理的一名下属。再根据Manager.Id分组,对Report.Id求和得到每个经理对应的下属数量,接着筛选cnt>=5的数据即可。 

select name
from (
    select 
        Manager.name as name,
        count(Report.Id) as cnt
    from
        Employee as Manager
    join
        Employee as Report
    on Manager.id = Report.ManagerId
    group by Manager.id
) as ReportCount
where cnt >= 5;

第二种思路:

不用子查询,直接使用having子句筛选大于5的数据:

select 
    Manager.name as name
from
    Employee as Manager
join
    Employee as Report
on Manager.id = Report.ManagerId
group by Manager.id
having count(Manager.id) >= 5

注意:

1. 注意having和where过滤条件的区别:

  • 用途
    • where子句用于在对表中的行进行过滤之前指定条件,在数据分组前应用,用于筛选行。
    • having子句用于在对分组的结果应用聚合函数后指定条件,在数据分组后,对分组结果进行过滤。
  • 使用位置
    • where出现在查询的from之后,group by之前。
    • having出现在group by之后。
  • 应用范围
    • where过滤的是行级数据,用于筛选表中的行,不涉及聚合函数。
    • having过滤的是分组级别的数据,用于筛选进行了聚合的分组,通常涉及聚合函数(例如count、sum、avg等)。

2. 正确使用join,有些时候cross join和inner join用起来差不多。

第三种思路:

上边的查询为了得到经理的名字,首先对两份employee表进行了连接,但是我们其实可以先对经理进行筛选,再通过连接操作得到经理的名字。要筛选员工数大于5的经理,直接将employee表根据managerId进行分组,每组中的id即为每个经理对应的下属,取下属数量大于5的条目。然后与employee表进行连接,得到manager的姓名。

select Employee.name
from (
    select managerId as id
    from Employee
    group by managerId
    having count(id) >= 5
) as Manager
join Employee
on Manager.id = Employee.id;

 

1934. 确认率

select
  s.user_id,
  round(sum(if(action='confirmed', 1, 0)) / count(s.user_id), 2) as confirmation_rate
from
  Signups s
left join
  Confirmations c
on
  s.user_id = c.user_id
group by
  s.user_id;

思路:

数值计算部分:

1. 首先‘if(action='confirmed', 1, 0)’使用if函数,检查action列的值是否等于confirmed,如果是则返回1,否则返回0;

2. 接着sum函数对上述条件表达式的结果进行求和;

3. count(s.user_id)计算id列的总行数;

4. sum(...) / count(s.user_id)相除得到确认率;

5. round(..., 2)将结果四舍五入到小数点后两位。

表部分:

signup表和confirmations表左连接

分母可以通过计算count(user_id)实现,分子通过sum和if的结合计算。很巧妙地解决了null的情况。

此外也可以通过AVG()函数来实现:

1. 使用AVG函数计算confirmed平均值,如果不存在则为null;

2. 使用IFNULL把null转化为0;

3. 使用ROUND精确小数点位数。

SELECT
    s.user_id,
    ROUND(IFNULL(AVG(c.action='confirmed'), 0), 2) AS confirmation_rate
FROM
    Signups AS s
LEFT JOIN
    Confirmations AS c
ON
    s.user_id = c.user_id
GROUP BY
    s.user_id

AVG(c.action='confirmed'):这部分计算了确认动作(action 列的值为 'confirmed')的平均值。这是一个聚合函数,用于计算符合条件的行的平均值。如果没有符合条件的行,平均值可能为 NULL

注意:

1. 函数的应用考察,if条件过滤,第二种思路会存在null的情况,因此需要使用ifnull进行处理;

2. 表格的命名;

3. 表格join的思路。

 

 

 

 

 

 

 

 

 

 

 

 

       

 

标签:JOIN,name,50,Key,SQL,join,高频,id,SELECT
From: https://www.cnblogs.com/lbwBH/p/17842912.html

相关文章

  • 代码随想录算法训练营第十一天 | ● 20. 有效的括号 ● 1047. 删除字符串中的所有相邻
    今日学习的内容●20.有效的括号varisValid=function(s){letstack=[];for(leti=0;i<s.length;i++){lettemp=s[i];if(temp=='('){stack.push(')')continue;}if(......
  • 给定SQL_ID,查看访问的表以及其统计信息
    --https://github.com/jkstill/oracle-script-lib/blob/master/sql/stats-sqlid.sql--stats-sqlid.sql--getstatsinfoforallobjectsusedinaSQL_ID--JaredStill-2017--jkstill@gmail.com----theplan_hash_valuesareshownasanaggregate.--thi......
  • mysql巡检脚本sql补充
    --没有主键索引mysql[localhost:8028]{root}(test)>SELECTt.table_schema,t.table_name,t.engine->FROMinformation_schema.tablest->JOINinformation_schema.columnsc->ONt.table_schema=c.table_schema->ANDt.table_name=c.......
  • INFINI Labs 产品更新 | 发布 Easysearch Java 客户端,Console 支持 SQL 查询等功能
    近年来,日志管理平台越来越流行。使用日志管理平台可以实时地、统一地、方便地管理和查看日志,挖掘日志数据价值,驱动运维、运营,提升服务管理效率。方案架构Beats是轻量级采集器,包括Filebeat、Metricbeat等。Easysearch是个分布式搜索引擎,提供搜集、分析、存储数据等主要功能。Con......
  • INFINI Labs 产品更新 | 发布 Easysearch Java 客户端,Console 支持 SQL 查询等功能
    近年来,日志管理平台越来越流行。使用日志管理平台可以实时地、统一地、方便地管理和查看日志,挖掘日志数据价值,驱动运维、运营,提升服务管理效率。方案架构Beats是轻量级采集器,包括Filebeat、Metricbeat等。Easysearch是个分布式搜索引擎,提供搜集、分析、存储数据等主要功......
  • 查看SQLServer平均最耗资源时间的SQL语句
    SELECT(total_elapsed_time/execution_count)/1000N'平均时间ms',total_elapsed_time/1000N'总花费时间ms',total_worker_time/1000N'所用的CPU总时间ms',total_physical_reads......
  • Linux安装MySQL
    本文使用的Linux发行版本为AlmaLinux9.264位(CentOS停止更新后的完美替代发行版本)。本文安装的MySQL版本为8.1.0,其他版本方法类似。MySQL源码编译时间太长了,需要3到4小时,使用官网编译好的rpm更简单快捷。操作步骤更新系统。dnf-yupdate查看是否已经有安装了的mysql......
  • 从 Mysql 架构上如何查询 sql 的执行过程?
    MySQL是一种流行的关系型数据库管理系统(RDBMS),由瑞典MySQLAB公司开发,目前属于甲骨文公司(Oracle)旗下。MySQL是基于SQL语言的一个开源数据库,可以用于存储、管理、检索数据。它支持大量的并发用户连接,并且提供了灵活的数据类型和索引机制。MySQL有多个版本,包括社区版和商业版,提供了不......
  • Docker - Run PostgreSQL database
    zzh@ZZHPC:~$dockerpullpostgres:alpinezzh@ZZHPC:~$dockerimagesREPOSITORYTAGIMAGEIDCREATEDSIZEpostgresalpine642d75c6be0c9daysago245MB zzh@ZZHPC:~$dockerrun--namepostgres16-p5432:5432-ePOSTGRES_......
  • postgresql 16主要新特性
    继postgresql15新特性后,pg16已经发布,这个版本的主要特性如下:1、逻辑复制支持级联,不一定要从primary开始;2、逻辑复制支持订阅自己,也就是真正的双主可以通过逻辑复制内置订阅实现了,不需要依赖三方,如pglogical3、copy性能提升明显,某些场景下300%4、SIMD支持数组和json、子事务搜......