【问题标题】:SQL correlated subqueriesSQL相关子查询
【发布时间】:2016-09-22 21:05:36
【问题描述】:

我在 Codecademy 上做这个练习几天了,仍然无法理解它背后的逻辑。下面是练习和代码:

通过按时间和承运人为航班提供序列号来订购航班会很有趣。 例如,假设每增加一个航班,flight_id 就会增加,我们可以使用以下查询按航空公司、航班 ID 和序列号查看航班:

SELECT carrier, id,
       (SELECT COUNT(*)
        FROM flights f
        WHERE f.id < flights.id
          AND f.carrier=flights.carrier) + 1 AS flight_sequence_number
FROM flights;

我可以理解f是表flights的虚拟形式,但是f.id &lt; flights.id是做什么的呢?是不是意味着SQL会比较f中的每一行和flights中的每一行类似

比较 MQ 17107 与 MQ 7869,

比较 MQ 17107 与 MQ 2205,

比较 MQ 17107 和 MQ 14979

……

比较 MQ 7869 与 MQ 2205,

比较 MQ 7869 和 MQ 14979

……

另外,这个COUNT(*) 到底算什么?为什么+1

这是结果图片:query result

任何帮助将不胜感激。谢谢。

【问题讨论】:

  • 统计当前id前的航班数,加1。
  • 良好的编程习惯有两个不同的表别名。例如。 f1 和 f2 在这里。
  • 有了modern SQL,使用简单的窗口函数可以更轻松地完成:row_number() over (partition carrier order by id) - 不需要子查询。
  • @jarlh 谢谢,你能告诉我 f.id

标签: sql subquery correlated-subquery


【解决方案1】:

查询从表航班中选择记录。它为每条记录选择运营商、id 和 flight_sequence_number。

因此,对于航班中的每条记录,都会执行子查询。它再次读取表航班,但只获取具有相同承运人且航班 ID 小于主记录之一的记录。要说主查询记录和子查询记录,需要一到两个表别名,否则这两条记录都会像flight.id一样被称为flight,不清楚你说的是哪一条。所以内部查询中的表得到了别名 f。现在您可以比较 f.id

COUNT(*) 计算所有低于其承运人的主要记录的航班 ID 的记录,从而对承运人的行进行编号。对于最小的 ID,您找不到较小的 ID,因此计数为 0,然后添加一个,从而获得行号 1。对于第二小的 ID,您找到一条 ID 较小的记录,因此您获得计数 1,加 1 并获得行号 2等等。

当您添加 order by 子句 ORDER BY carrier, id 时,结果会更好看,因此您会按照使用的顺序显示结果。

如前所述,一些现代 DBMS 提供了分析功能,例如ROW_NUMBER,并且查询变得更加简单:

select 
  carrier, 
  id, 
  row_number() over (partition by carrier order by id) as flight_sequence_number
from flights
order by carrier, id;

【讨论】:

  • 我还在吸收你的答案。不过非常感谢。
【解决方案2】:

您有一个带有 flight_id 的航班列表,该列表告诉您所有航班相对于彼此的顺序。您正在查看的查询试图做的是获取每个航班的序列,仅与同一航空公司的航班有关。例如,如果我问你第 50 次航班是什么,这很容易,因为你可以用 flight_id = 50 查找航班。但如果我问你第 50 次美国航空公司的航班是什么,你不会有任何想法与您给出的表没有一点聚合或使用分区。

让我们看一个简单的选择查询。如果您选择给定类型 A 的所有运营商,您将得到如下结果:

SELECT flight_id, carrier
FROM flights
WHERE carrier = A

flight_id, carrier
------------------
    4,       A
    9,       A
    10,      A
    18,      A
    20,      A
    25,      A
    26,      A

但是,当我们只查看承运人 A 时,flight_id 不再是序列号。id 为 4 的航班不是我们结果中的第 4 个航班(即航班 18)。然后,我们可以汇总数据,以便我们获得每个航班相对于同一承运人航班的顺序。所以我们要做的就是在每一行之前统计相同类型的航班数量,得到它的序列。航班 4 没有更早的类型 A 的航班,因此它将有一个序列 1(在它之前有 0 个航班,我们添加 1 以使序列从 1 开始)。航班 9 之前有 1 个航班,所以我们有序列 2(之前的 1 个航班 + 1 个偏移量 = 2)。

我们需要对结果中的每个航班都执行此操作,因此我们可以在查询的选择部分中包含子查询。 select 中的子查询与另一个 select 查询类似,但允许您使用外部查询中的数据,并对外部查询结果中返回的每一行执行此操作。

对于我们想要做的事情(根据订单和承运人查找航班的序号),子查询会将每一行的id与同一张表中的所有行进行比较,如果它小于当前行的id,它是它之前的一个航班,将在子查询的结果中。 COUNT 聚合此子查询的结果,因此它是一个值。我们还确保子查询中的所有结果都与我们在主查询中查看的当前行的载体相匹配。我们必须为这个子查询使用别名,因为它是从同一个表中选择的,否则会模棱两可,这就是为什么它使用别名“f”来将其与主查询区分开来。我们还可以给这个子查询的结果命名为 flight_sequence_number,它存储在一个新列中。这将使您轻松了解所有航班的顺序,以及相对于同一承运人的航班顺序。

【讨论】:

  • 感谢您说得这么清楚。这为我澄清了一切。