联接和子查询

时间:2019-03-22 13:09:15

标签: sql

我知道相关子查询使用“ where”子句而不是联接。 但是我想知道“ where”子句和内部联接是否可以具有相同的结果,那么为什么我们不能将这些查询与联接一起使用? 例如,

    SELECT FirstName, LastName, (SELECT COUNT(O.Id) FROM [Order] O WHERE O.CustomerId = C.Id) As OrderCount
  FROM Customer C 

现在,为什么我们不能像下面这样写下来?

    SELECT FirstName, LastName, (SELECT COUNT(O.Id) FROM [Order] O Inner Join 
 C On O.CustomerId = C.Id) As OrderCount
  FROM Customer C 

我对SQL非常了解,并且在这方面做得很安静,但是我只是在寻找清晰的技术说明。

谢谢。

2 个答案:

答案 0 :(得分:2)

这是您的查询:

SELECT
  FirstName,
  LastName, 
  (
    SELECT COUNT(O.Id) 
    FROM [Order] O 
    INNER JOIN C On O.CustomerId = C.Id
  ) AS OrderCount
FROM Customer C;

这是无效的,因为您正在子查询中从C中进行选择。

这有点复杂。在查询中,我们处理表和表行。例如:

select person.name from person;

FROM person的意思是“来自表person”。 person.name的意思是“某人的name”,因此它是指一行。如果我们可以写的话,那将是很好的:

select person.name from persons;

但是SQL不了解您所用语言中的单数和复数,因此这是不可能的。

在您的查询中,FROM Customer C的意思是“来自客户表,我将简称为C”。但是在其余查询(包括子查询)中,它是C所指的一个客户行。因此,您不能说INNER JOIN C,因为您只能联接到表,而不能联接到表行。

可以尝试通过使用表的复数名称和单数名称作为表别名来使这一点变得清楚。如果要养成习惯,则在主查询中应使用FROM Customers Customer,在内部查询中应使用INNER JOIN Customer,并且从习惯中可以注意到,在查询中不能有单数FROM子句。但是好吧,人们很快就习惯了查询中表名的双重含义(行和表),因此这有点过分防御,我们宁愿使用别名来使查询更短,更易读,就像您将客户缩写为c一样。

但是,是的,您可以在SELECT子句中使用联接而不是子查询。将子查询移至FROM子句:

SELECT
  c.firstname,
  c.lastname, 
  COALESCE(o.ordercount, 0) AS ordercount
FROM customer c
LEFT JOIN
(
  SELECT customerid, COUNT(*) AS ordercount
  FROM [order]
  GROUP BY customerid
) o ON o.customerid = c.id;

或在不使用子查询的情况下加入:

SELECT
  c.firstname,
  c.lastname, 
  COUNT(o.customerid) AS ordercount
FROM customer c
LEFT JOIN [order] o ON o.customerid = c.id
GROUP BY c.firstname, c.lastname;

答案 1 :(得分:1)

这两个查询在功能上是等效的。 SQL(在查询的上下文中)是一种声明性语言,这意味着它通过定义您要实现的目标而不是如何实现它来工作。因此,在抽象代数级别上,两个查询之间绝对没有区别。 (*)

但是,由于SQL不在代数的形而上学领域中起作用,而是在现实世界中需要在操作过程中转换SQL的声明性语言:在我看来,决定这两个查询要容易得多等同于您选择的RMDB。计算SQL声明式查询的关闭在计算上非常困难。这是通过通常称为“查询优化器”的方法完成的,该功能不仅具有“理解”关系代数的功能,而且还可以找到概率上最佳的程序实现方式。因此,根据优化器的准确性,模式和查询的复杂性以及优化器在关闭和优化执行计划时分配的计算资源量,两个其他等效查询的实际执行计划可能会有所不同。您仍然会得到相同的结果(只要您停留在声明性领域并且不使用任何NOW()RAND()或其他易变状态语义),但是一种计划方法可能会更快,而另一种可能会慢一些。同样,结果的顺序可能有所不同,其中ORDER BY缺失或模棱两可。

注意:您的联接可以用这种方式重写,因为它涉及侧联接的聚合。并非所有联接都可以使用子查询进行转置,但是其他查询在很多情况下是等效的,尽管表达方式有所不同。对于任何数学上等效的查询,我的回答都是绝对通用的。另请参见下面的说明。

(*)查询等效性还取决于架构。常识是常有的敌人:NULL值:如果联接将在存在任何条件的情况下过滤掉null值,则聚合将以多种其他方式运行:SUM将为null,MAX / { {1}}将忽略空值,MIN将计数任何内容,COUNT没有人知道会做什么,等等。