代码之家  ›  专栏  ›  技术社区  ›  jmucchiello

SQL左联接vs第行上的多个表?

  •  232
  • jmucchiello  · 技术社区  · 17 年前

    大多数SQL方言都接受以下两种查询:

    SELECT a.foo, b.foo
    FROM a, b
    WHERE a.x = b.x
    
    SELECT a.foo, b.foo
    FROM a
    LEFT JOIN b ON a.x = b.x
    

    11 回复  |  直到 15 年前
        1
  •  333
  •   benomatis    10 年前

    旧语法,只列出表,并使用 WHERE 用于指定联接条件的子句在大多数现代数据库中都被弃用。

    这不仅仅是为了展示,当您在同一个查询中同时使用内部联接和外部联接时,旧语法可能存在歧义。

    假设系统中有3个表:

    Company
    Department
    Employee
    

    好的,现在您要执行以下操作:

    所以你要这样做:

    SELECT * -- for simplicity
    FROM Company, Department, Employee
    WHERE Company.ID *= Department.CompanyID
      AND Department.ID = Employee.DepartmentID
    

    好的,那么现在发生了什么。问题是,它取决于数据库引擎、查询优化器、索引和表统计信息。让我解释一下。

    如果查询优化器确定执行此操作的方法是首先选择一家公司,然后查找部门,然后与员工进行内部联接,那么将不会得到任何没有部门的公司。

    原因是 子句确定 在最终结果中结束,而不是行的各个部分。

    在这种情况下,由于左连接,Department.ID列将为NULL,因此当涉及到员工的内部连接时,无法满足员工行的约束,因此它不会出现。

    另一方面,如果查询优化器决定首先处理department employee联接,然后与公司进行左联接,您将看到它们。

    输入新语法,您可以使用它进行选择。

    SELECT *
    FROM Company
         LEFT JOIN (
             Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
         ) ON Company.ID = Department.CompanyID
    

    SELECT *
    FROM Company
         LEFT JOIN (
             Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
         ) ON Company.ID = Department.CompanyID AND Department.Name LIKE '%X%'
    

    这就是为什么自SQLServer2005及以上版本以来,除其他供应商外,Microsoft不推荐使用旧的外部联接语法,而不推荐使用旧的内部联接语法。使用旧式外部联接语法与运行在Microsoft SQL Server 2005或2008上的数据库通信的唯一方法是将该数据库设置为8.0兼容模式(也称为SQL Server 2000)。

    好了。

    左内结合是未来的潮流。

        2
  •  18
  •   Andomar    17 年前

    联接语法将条件保留在其应用的表附近。这在连接大量表时特别有用。

    顺便说一下,您也可以使用第一种语法进行外部联接:

    WHERE a.x = b.x(+)
    

    WHERE a.x *= b.x
    

    WHERE a.x = b.x or a.x not in (select x from b)
    
        3
  •  14
  •   Dwight T    17 年前

    第一种方法是旧标准。第二种方法是在SQL-92中引入的, http://en.wikipedia.org/wiki/SQL . 完整的标准可在以下位置查看: http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt

    数据库公司花了很多年才采用SQL-92标准。

    因此,第二种方法被首选的原因是,根据ANSI和ISO标准委员会,它是SQL标准。

        4
  •  14
  •   Peter Perháč    9 年前

    基本上,当FROM子句列出如下表时:

    SELECT * FROM
      tableA, tableB, tableC
    

    结果是表a、B、C中所有行的叉积。然后应用限制 WHERE tableA.id = tableB.a_id 这将丢弃大量的行,然后进一步。。。 AND tableB.id = tableC.b_id 然后你应该只得到你真正感兴趣的行。

    DBMS知道如何优化此SQL,从而使使用联接编写此SQL的性能差异可以忽略不计(如果有)。使用连接表示法生成SQL语句 更多 可读性(IMHO,不使用联接会使语句变得混乱)。使用叉积,您需要在WHERE子句中提供连接条件,这就是符号的问题所在。你把WHERE子句挤满了像这样的东西

        tableA.id = tableB.a_id 
    AND tableB.id = tableC.b_id 
    

    仅用于限制交叉积。WHERE子句应仅包含对结果集的限制。如果将表联接条件与结果集限制混合使用,您(和其他人)会发现查询更难阅读。您肯定应该使用连接,并保留FROM子句和WHERE子句。

        5
  •  11
  •   HLGEM    17 年前

    第二种是首选的,因为它不太可能由于忘记放入where子句而导致意外的交叉连接。没有on子句的联接将无法通过语法检查,没有where子句的旧式联接不会失败,它将执行交叉联接。

    此外,我还发现,许多只使用第一种语法的人并不真正理解联接,而理解联接对于在查询时获得正确结果至关重要。

        6
  •  6
  •   Alan G    13 年前

    在真正复杂的SELECT语句中,读者更容易理解正在发生的事情。

        7
  •  5
  •   Euro Micelli    17 年前

    这个 SELECT * FROM table1, table2, ... 对于一些表来说,语法是可以的,但是它会以指数的形式变化( 不一定是数学上准确的陈述 )随着表格数量的增加,阅读变得越来越困难。

    连接语法(在开始时)更难编写,但它明确了哪些条件会影响哪些表。这使得犯错误更加困难。

    此外,如果所有联接都是内部联接,则两个版本都是等效的。然而,当你在语句中的任何地方有一个外部连接时,事情就变得复杂多了,这实际上保证了你写的东西不会质疑你认为你写的东西。

        8
  •  2
  •   Pablo Santa Cruz    17 年前

    神谕:

    SELECT a.foo, b.foo
      FROM a, b
     WHERE a.x = b.x(+)
    

    deprecated 2000年版)/Sybase中:

    SELECT a.foo, b.foo
      FROM a, b
     WHERE a.x *= b.x
    

    参加 比在一个表达式中添加一个表达式更自然(至少在语法上是这样) 哪里 条款,当您正在执行以下操作时: 连接 .

        9
  •  0
  •   kemiller2002    17 年前

    我听到很多人抱怨第一个太难理解,而且不清楚。我看不出有什么问题,但在讨论之后,为了清晰起见,我甚至在内部联接上使用了第二个。

        10
  •  0
  •   Jeff Ferland    17 年前

    对于数据库,它们最终是相同的。不过,对于您来说,在某些情况下必须使用第二种语法。为了编辑最终不得不使用它的查询(发现在有直接连接的情况下需要左连接),并且为了一致性,我只选择第二种方法。这将使阅读查询更容易。

        11
  •  0
  •   Gavin H    17 年前

    第一个和第二个查询可能会产生不同的结果,因为左连接包括第一个表中的所有记录,即使右表中没有相应的记录。