SQL中的CROSS JOIN和INNER JOIN有什么区别?

198

什么是 CROSS JOININNER JOIN 的区别?

CROSS JOIN:

SELECT 
    Movies.CustomerID, Movies.Movie, Customers.Age, 
    Customers.Gender, Customers.[Education Level], 
    Customers.[Internet Connection], Customers.[Marital Status], 
FROM   
    Customers 
CROSS JOIN 
    Movies

内连接(INNER JOIN):

SELECT 
    Movies.CustomerID, Movies.Movie, Customers.Age, 
    Customers.Gender, Customers.[Education Level], 
    Customers.[Internet Connection], Customers.[Marital Status]
FROM   
    Customers 
INNER JOIN 
    Movies ON Customers.CustomerID = Movies.CustomerID

哪一个更好,我为什么要使用其中之一?


17
CROSS JOIN 将会得到两个表的所有可能组合,例如:有一个拥有100行数据的Table1和另一个拥有100行数据的Table2,那么结果将会是10000条记录。 - bummi
12
x CROSS JOIN y is x INNER JOIN y ON 1=1 - philipxy
11个回答

216
这是Cross Join和Inner Join的最佳示例。
考虑以下表格: 表格:Teacher
x------------------------x
| TchrId   | TeacherName | 
x----------|-------------x
|    T1    |    Mary     |
|    T2    |    Jim      |
x------------------------x

表格:学生。
x--------------------------------------x
|  StudId  |    TchrId   | StudentName | 
x----------|-------------|-------------x            
|    S1    |     T1      |    Vineeth  |
|    S2    |     T1      |    Unni     |
x--------------------------------------x

1. INNER JOIN

内连接选择满足两个表的行。

假设我们需要找到班主任以及他们对应的学生。在这种情况下,我们需要使用 JOININNER JOIN.

enter image description here

查询语句

SELECT T.TchrId,T.TeacherName,S.StudentName 
FROM #Teacher T
INNER JOIN #Student S ON T.TchrId = S.TchrId

结果

x--------------------------------------x
|  TchrId  | TeacherName | StudentName | 
x----------|-------------|-------------x            
|    T1    |     Mary    |    Vineeth  |
|    T1    |     Mary    |    Unni     |
x--------------------------------------x

2. CROSS JOIN

CROSS JOIN(叉积连接)选择第一个表格中的所有行和第二个表格中的所有行,并将它们显示为笛卡尔乘积,即具有所有可能性。

考虑我们需要找到学校里的所有老师和学生,而不考虑班级教师,我们需要应用CROSS JOIN

enter image description here

查询语句:

SELECT T.TchrId,T.TeacherName,S.StudentName 
FROM #Teacher T
CROSS JOIN #Student S 

结果

x--------------------------------------x
|  TchrId  | TeacherName | StudentName | 
x----------|-------------|-------------x            
|    T2    |     Jim     |    Vineeth  |
|    T2    |     Jim     |    Unni     |
|    T1    |     Mary    |    Vineeth  |
|    T1    |     Mary    |    Unni     |
x--------------------------------------x

2
图2的关键点很复杂:它围绕CROSS JOIN参数的元素(颜色无关)和一个数字(值无关)构成行,一条线(颜色无关)是结果行。对于作为袋子的表,它不是一个文氏图:对于作为值的行,它是错误的;对于作为元素的行,它们不能被共享。对于作为集合的表,你不需要一个文氏图。图1是一个常见的糟糕尝试来解释JOIN。它的关键点也很复杂:它只适用于作为集合的表、只有等值连接和只有一个值;它还以与输出不同的方式表示输入。在一般情况下,用表示JOIN。 - philipxy
图1对于四个相交圆形维恩图的着色是有用且正确的,这四个维恩图分别为:(INNER) JOIN 与LEFT、RIGHT和FULL (OUTER) JOIN但不包括CROSS JOIN。交集行在JOIN中,左右行是LEFT/RIGHT JOIN中的额外(null-extended)行。它将CROSS JOIN包括在(INNER) JOIN的特殊情况中,其中非交集中没有行。 - philipxy
2
感谢您的建议。无论如何,OP已经要求解释这两个连接之间的区别。我已经以一种任何初学者都可以轻松理解它们之间的差异的方式回答了这个问题。正如您所说,我没有在生产环境中给出键。这只是一个易于理解的示例。至于“交叉连接”,您认为它不返回所有行,除非给出“Where”子句吗?根据您的评论,初学者更容易混淆!@philipxy - Sarath Subramanian
1
我的评论指出,即使人们知道图表想要表达什么,它们也很难解释,并且对于这个主题来说不合适。在“关键”(图2或1的关键)中,我指的是“图表部分含义的解释”。通过“写下来”,我是指尝试自己清楚地写出图表部分的含义。您会发现,这些图表很复杂,不能演示交叉连接与内部连接!也就是说,它们不属于您的答案。附言:表关系键在解释JOIN的作用时没有任何作用。再附言:连接之间唯一的区别是INNER JOIN有一个ON。 - philipxy
请查看W3Schools http://www.w3schools.com/sql/sql_join_inner.asp,他们提供了内连接的类似类型图表。在发表评论之前,请确保您是正确的 @philipxy - Sarath Subramanian
显示剩余2条评论

140

交叉连接不会合并行,如果每个表都有100行且1对1匹配,则会得到10,000个结果,而在相同情况下,Inner Join仅返回100行。

在以下两个示例中,将返回相同的结果:

交叉连接

select * from table1 cross join table2 where table1.id = table2.fk_id

内部连接

select * from table1 join table2 on table1.id = table2.fk_id

使用最后一种方法


10
我相信您可以将select * from table1 cross join table2 where table1.id = table2.fk_id改写为select * from table1, table2 where table1.id = table2.fk_id(用,替换cross join)。 - modulitos
6
@Lucas 这是旧版连接语法,但它仍然可用。我建议使用 Clausen 的版本,更易读。 - John Smith
1
你说的“一对一匹配”是什么意思?你所说的“相同情况”又是指什么? - Jwan622
1
@iliketocode,“旧的连接语法”不太清楚,最好讲一下SQL-89和SQL-92标准。 - user2188550
@t-clausen.dk 优化器与语言文本定义返回值无关。我不理解“优化器处理这个”的意思。我同意它决定了实现方式。我的主要观点是,前面的评论对于实现/优化存在误解。(我说ON比WHERE具有更高的优先级是非正式和粗糙的,可以忽略,抱歉。这并不影响我评论的其他部分。我谈论的是文本定义返回值,而不是实现。) - philipxy
显示剩余3条评论

109

CROSS JOIN = (INNER) JOIN = 逗号(",")

简介 SQL中CROSS JOIN、(INNER) JOIN和逗号(",")之间唯一的区别(除了逗号运算符优先级更低)是(INNER) JOIN使用ON子句,而CROSS JOIN和逗号不需要。


关于中间产物

这三种方法都会产生一个中间概念性的SQL样式关系型“笛卡尔”积,即两个表中所有可能的行组合。通过使用ON和/或WHERE来减少行数。SQL Fiddle

SQL标准通过product(7.5 1.b.ii)定义了,通过<comma>定义了<cross join> aka CROSS JOIN(7.7 1.a),通过<comma>加上WHERE以及<search condition>定义了(INNER) JOIN ON(7.7 1.b)。

维基百科解释如下:

Cross join
CROSS JOIN返回连接中各表的行的笛卡尔积。换句话说,它将产生将第一个表中的每一行与第二个表中的每一行组合在一起的行。

Inner join
[...]连接结果可以定义为首先对表中所有记录进行笛卡尔积(或Cross join),然后返回满足连接谓词的所有记录。

“隐式连接表示法”只需在SELECT语句的FROM子句中列出要连接的表,用逗号分隔它们即可。因此,它指定了一个cross join。

一些SQL允许使用无ON的非标准(INNER)JOIN进行cross join。

关于OUTER JOIN请参见我的答案What is the difference between "INNER JOIN" and "OUTER JOIN"?

关于OUTER JOIN和在其中使用ON vs WHERE,请参见我的答案Conditions in LEFT JOIN (OUTER JOIN) vs INNER JOIN

为什么要比较表之间的列?

当没有重复行时:

每个表都保存从某个填空-[命名-]模板中得到真命题的行。(它从某个(特征)谓词中得到真命题。)

  • 基表保存了从一些由DBA给出的模板中获得真命题的行:

      /* rows where
      customer C.CustomerID has age C.Age and ...
      */
      FROM Customers C
    
  • 连接操作的中间产物是包含来自操作数模板AND运算结果为真的行的表格:

  •   /* rows where
          customer C.CustomerID has age C.Age and ...
      AND movie M.Movie is rented by customer M.CustomerID and ...
      */
      FROM Customers C CROSS JOIN Movies M
    
  • ON和WHERE条件AND连接,形成更进一步的模板,值仍然是符合该模板的行:

  •   /* rows where
          customer C.CustomerID has age C.Age and ...
      AND movie M.Movie is rented by customer M.CustomerID and ...
      AND C.CustomerID = M.CustomerID
      AND C.Age >= M.[Minimum Age]
      AND C.Age = 18
      */
      FROM Customers C INNER JOIN Movies M
      ON C.CustomerID = M.CustomerID
      AND C.Age >= M.[Minimum Age]
      WHERE C.Age = 18
    

比较两个表之间(SQL)列的相等性,意味着模板的连接表部分生成的乘积中保留的行具有这些列的相同(非NULL)值。通常通过表之间的等式比较删除了许多行,这只是巧合--必要和充分的是描述您想要的行的模板的SQL。

只需编写所需行的模板SQL代码!

关于查询的含义(表与条件),请参见:
如何获取另一个SQL表的匹配数据,用于两个不同的列:内部联接和/或联合?
是否有任何经验法则可以从可读的描述构造SQL查询?

"交叉联接"的重载

不幸的是,“交叉联接”一词用于以下情况:

  • 中间产品。
  • CROSS JOIN。
  • (INNER) JOIN,其中ON或WHERE未将一个表的任何列与另一个表的任何列进行比较。(因为这可能会返回许多中间产品行)

使用CROSS JOIN vs (INNER) JOIN vs 逗号

常见约定是:

  • 仅当您不比较表之间的列时,才使用CROSS JOIN。这是为了显示缺少比较是有意的。
  • 只有在比较表之间的列时(可能还有其他条件)时,才使用(INNER) JOIN with ON。
  • 不要使用逗号。

通常,不涉及表对的条件保留为WHERE条件。但是,它们可能必须放入(INNER) JOIN ON中,以获取适当的行,以供RIGHT、LEFT或FULL(OUTER) JOIN的参数。

关于“不要使用逗号” 混合使用逗号和明确的JOIN可能会产生误导,因为逗号的优先级较低。但是,考虑到CROSS JOIN、(INNER) JOIN和逗号的意义中间产品的作用,不使用逗号的约定的论据是站不住脚的。CROSS JOIN或逗号就像ON TRUE条件的(INNER) JOIN一样。中间产品、ON和WHERE在相应的谓词中都引入AND。无论如何,内联接ON可以被认为是什么-例如,仅在找到满足ON条件的一对输入行时生成输出行——它仍然返回满足条件的交叉连接行。ON必须补充逗号在SQL中的唯一原因是编写OUTER JOINS。当然,一个表达式应该使其含义清晰;但是,清晰取决于对事物的理解。

关于Venn图,一个有两个相交圆的Venn图可以展示INNER、LEFT、RIGHT和FULL JOIN的输出行在相同输入情况下的差异。当ON无条件为TRUE时,INNER JOIN结果与CROSS JOIN相同。同时,它可以展示INTERSECT、UNION和EXCEPT的输入和输出行。当两个输入具有相同的列时,INTERSECT结果与标准SQL NATURAL JOIN相同,而EXCEPT结果则与一些涉及LEFT和RIGHT JOIN的特定惯用语相同。但它不能总体上说明(INNER) JOIN的工作原理,这只是乍一看似乎是这样。它可以识别ON、PK(主键)、FK(外键)和/或SELECT的特殊情况中的输入和/或输出的部分。你所需要做的就是确定由这些圆所表示的集合的元素是什么。正如混乱的展示从未表明的那样。请记住,在JOIN中,输出行与输入行具有不同的标题。而且SQL表是带有NULL的而不是集合

请查看我的回答:Venn Diagram for Natural Join

1
“中级概念的SQL风格笛卡尔积”——这是另一种说法,“它并不是一个有序对集合,但我无法避免使用‘笛卡尔积’这个术语”? :) 作为一个非数学家,我所遇到的唯一上下文是当有人解释SQL的CROSS JOIN时。我确实想知道有多少人已经熟悉笛卡尔积,但无法理解CROSS JOIN - onedaywhen
1
@onedaywhen 运算符笛卡尔积返回一些集合的有序元组集。结果是一个笛卡尔积。关系和SQL文化不幸地滥用/重载“笛卡尔积”运算符,这些运算符引起不是笛卡尔积。例如维基百科!我反对这一点——它只会误导/混淆。然而,在这里,我只模糊地描述了SQL交叉连接如何工作,并推迟到维基百科。我标记结果为“中间概念SQL风格的笛卡尔交叉积”。是的,“笛卡尔”是为那些不幸已经被腐败的人使用/期望的。 - philipxy

27

内连接

仅显示在连接的两个表中都存在匹配记录的行的连接称为内连接。这是查询和视图设计器中默认的连接类型。

内连接的语法如下:

SELECT t1.column_name,t2.column_name
FROM table_name1 t1
INNER JOIN table_name2 t2
ON t1.column_name=t2.column_name

交叉连接

交叉连接会产生涉及到的表的笛卡尔积。笛卡尔积的大小是第一个表中行数乘以第二个表中行数。

交叉连接语法:

SELECT * FROM table_name1
CROSS JOIN table_name2

或者我们也可以用另一种方式来写它

SELECT * FROM table_name1,table_name2

现在检查下面的查询以进行交叉连接

示例

SELECT * FROM UserDetails
CROSS JOIN OrderDetails

或者

SELECT * FROM UserDetails, OrderDetails

20

交叉连接

交叉连接旨在生成笛卡尔积。

笛卡尔积将两个集合 A 和 B 生成来自两个给定数据集的所有可能的配对记录的排列组合。

例如,假设你有以下 rankssuits 数据库表:

The ranks and suits tables

并且 ranks 表具有以下行:

| name  | symbol | rank_value |
|-------|--------|------------|
| Ace   | A      | 14         |
| King  | K      | 13         |
| Queen | Q      | 12         |
| Jack  | J      | 11         |
| Ten   | 10     | 10         |
| Nine  | 9      |  9         |

suits 表包含以下记录时:

| name    | symbol |
|---------|--------|
| Club    | ♣      |
| Diamond ||
| Heart   | ♥      |
| Spade   ||

像下面这个CROSS JOIN查询:

SELECT
   r.symbol AS card_rank,
   s.symbol AS card_suit
FROM
   ranks r
CROSS JOIN
   suits s

将生成所有可能的 rankssuites 对的排列:

| card_rank | card_suit |
|-----------|-----------|
| A         | ♣         |
| A         ||
| A         | ♥         |
| A         ||
| K         | ♣         |
| K         ||
| K         | ♥         |
| K         ||
| Q         | ♣         |
| Q         ||
| Q         | ♥         |
| Q         ||
| J         | ♣         |
| J         ||
| J         | ♥         |
| J         ||
| 10        | ♣         |
| 10        ||
| 10        | ♥         |
| 10        ||
| 9         | ♣         |
| 9         ||
| 9         | ♥         |
| 9         ||

内连接

然而,内连接不会返回两个连接数据集的笛卡尔积。

相反,内连接将从左侧表格中获取所有元素,并将它们与右侧表格上的记录匹配,以便:

  • 如果在右侧表格中没有匹配的记录,则左侧行将从结果集中过滤掉。
  • 对于右侧表格上的任何匹配记录,左侧行将重复,就像该记录和右侧表格上所有关联子记录之间存在笛卡尔积一样。

例如,假设我们有一个父级post和一个子级post_comment表格之间的一对多关系,如下所示:

One-to-many table relationship

现在,如果post表格具有以下记录:

| id | title     |
|----|-----------|
| 1  | Java      |
| 2  | Hibernate |
| 3  | JPA       |

并且post_comments表有以下行:

| id | review    | post_id |
|----|-----------|---------|
| 1  | Good      | 1       |
| 2  | Excellent | 1       |
| 3  | Awesome   | 2       |

以下是一个类似于内连接的查询:

SELECT
   p.id AS post_id,
   p.title AS post_title,
   pc.review  AS review
FROM post p
INNER JOIN post_comment pc ON pc.post_id = p.id

将包括所有的post记录以及它们关联的所有post_comments

| post_id | post_title | review    |
|---------|------------|-----------|
| 1       | Java       | Good      |
| 1       | Java       | Excellent |
| 2       | Hibernate  | Awesome   |

基本上,您可以将 INNER JOIN 看作是过滤后的 CROSS JOIN,只有匹配的记录保留在最终结果集中。

1
当使用x INNER JOIN y on 1=1时,"INNER JOIN不返回两个连接数据集的笛卡尔积"有点过了,因为它确实会返回。同样,“Instead”也是如此,因为你接下来要尝试说的确实(当然)会返回条件1=1的结果。除了项目符号中的语言没有清楚地描述内部连接的结果之外。描述它的是一个交叉连接减去不满足条件的行。同样,您使用“组合”进行的交叉连接语言也不清楚。 - philipxy
@Vlad Mihalcea 内连接确实只是交叉连接 + where 子句。您必须通过计算所有行组合来测试匹配,这意味着您只是一步一步地计算笛卡尔积并丢弃不匹配的行对。 - Sebi2020

17

7

SQL Server还接受更简单的表示方法:

SELECT A.F, 
       B.G, 
       C.H 
  FROM TABLE_A A, 
       TABLE_B B, 
       TABLE_C C
 WHERE A.X = B.X 
   AND B.Y = C.Y

使用这种更简单的表示法,您无需担心内部联接和交叉联接之间的差异。与两个“ON”子句不同,只有一个“WHERE”子句可以完成任务。如果您很难确定哪个“JOIN”“ON”子句放在哪里,请放弃“JOIN”表示法并使用上面更简单的表示法。
这并不是欺骗。

4

在使用内连接编写查询时,如果两个表上的条件都满足,即在两个表中的公共列上存在精确匹配,则将从两个表中获取记录。

在使用交叉连接编写查询时,结果就像是两个表中记录数量的笛卡尔积。例如,如果table1包含2条记录,table2包含3条记录,则查询结果为2 * 3 = 6条记录。

因此,除非需要这样做,否则不要使用交叉连接。


2
只有在缺少WHERE子句的情况下! - epitka

2

交叉连接和内连接基本相同,唯一区别在于在内连接中,我们使用布尔过滤器过滤笛卡尔积的某些结果。

table1
x--------------------------------------x
|  fieldA  |    fieldB   |    fieldC   | 
x----------|-------------|-------------x            
|    A     |      B      |    option1  |
|    A     |      B1     |    option2  |
x--------------------------------------x

table2
x--------------------------------------x
|  fieldA  |    fieldB   |    fieldC   | 
x----------|-------------|-------------x            
|    A     |      B      |    optionB1 |
|    A1    |      B1     |    optionB2 |
x--------------------------------------x

 cross join
  A,B,option1,A,B,optionB1
  A,B,option1,A1,B1,optionB2
  A,B1,option2,A,B,optionB1
  A,B1,option2,A1,B1,optionB2

 inner join on field1 (only with the value is the same in both tables)
  A,B,option1,A,B,optionB1
  A,B1,option2,A,B,optionB1

 inner join on field1
  A,B,option1,A,B,optionB1

这涉及到我们设计数据时决定使用的字段只有一种情况来进行连接。连接仅交叉连接两个表,并获取仅符合特定布尔表达式的行。

请注意,如果我们用于连接的字段在两个表中都为null,则会通过过滤器。避免或允许空值需要我们或数据库制造商添加额外规则。基本上,它只是一个交叉连接,然后是一个过滤器。


2

这取决于你希望得到的输出。

交叉连接将一个表中的所有行与另一个表中的所有行匹配。内连接匹配一个或多个字段。如果一个表有10行,另一个表也有10行,则两种连接的行为不同。

交叉连接将返回100行,并且它们之间没有关联,只是所谓的笛卡尔积。内连接将记录相互匹配。假设一个表具有主键,并且在另一个表中作为外键,那么将返回10行。

交叉连接的通用性有限,但存在完整性,并描述了在查询中添加无关系的表时的结果。您可以使用交叉连接来生成单词组合列表或类似内容。而内连接则是最常见的连接方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接