【问题标题】:Understanding cartesian product in SQL了解 SQL 中的笛卡尔积
【发布时间】:2015-09-06 10:50:32
【问题描述】:

我无法理解笛卡尔积的工作原理。考虑简单的模式:

mysql> select * from account;
+----------------+-------------+---------+
| account_number | branch_name | balance |
+----------------+-------------+---------+
| A101           | Downtown    |     500 |
| A102           | Perryridge  |     400 |
| A201           | Brighton    |     900 |
| A215           | Mianus      |     700 |
| A217           | Brighton    |     750 |
| A222           | Redwood     |     700 |
| A305           | Round Hill  |     350 |
+----------------+-------------+---------+
7 rows in set (0.00 sec)

现在,当我提出查询时

select a.balance from account a, account b where a.balance<b.balance;

我得到一系列值,除了最大值 900。然后使用not in 运算符确定最大值。在上述查询之前,当基于条件a.balance&lt;b.balance 进行连接时,关系中的第一个元组必须是500。理论上,前 5 个值必须是:

500
500
500
500
400

但我明白了:

+---------+
| balance |
+---------+
|     400 |
|     350 |
|     350 |
|     500 |
|     400 |

它是如何工作的?我正在使用 MySQL 数据库。

【问题讨论】:

  • 一个笛卡尔积的命名会让人误以为它是线性的,而不是指数的,实际上它是。也就是说,这是一个很棒的流行词,可以吓跑初级程序员

标签: mysql sql select join cartesian-product


【解决方案1】:

笛卡尔连接将第一个表中的每条记录与第二个表中的每条记录连接起来,因此由于您的表有 7 行并且它与自身连接,因此如果您没有 where 子句,它应该返回 49 条记录。您的 where 子句只允许记录 a 的余额小于 b 的余额。因为900正如你所说的,是表中的最大余额,它永远不会小于任何其他余额,因此永远不会返回。

对于前五行,SQL 的常规规则也适用于连接。由于 SQL 表没有内在顺序,因此完全由数据库决定如何返回它们,除非您在 order by 子句中明确声明顺序。您列出的值是您希望查询返回的完全有效的值。

【讨论】:

  • 那么不同的数据库会返回不同的值顺序吗?我如何知道数据库使用什么顺序返回结果?
  • @JavaEnthusiast 不仅仅是不同的数据库,同一个数据库实例很可能会根据各种内部条件以不同的顺序返回行。确保以何种顺序返回行的唯一方法是使用order by 子句显式设置它。
【解决方案2】:

笛卡尔积从两组给定的数据中生成所有可能的记录组合。

在您的情况下,要生成笛卡尔积,您必须使用 CROSS JOIN:

SELECT 
  a.branch_name AS first_branch,
  b.branch_name AS second_branch,
  a.balance + b.balance AS total_balance
FROM account a
CROSS JOIN account b 

或者,使用 SQL:89 theta 样式连接:

SELECT 
  a.branch_name AS first_branch,
  b.branch_name AS second_branch,
  a.balance + b.balance AS total_balance
FROM account a, account b 

无论如何,笛卡尔积的目标是关联两个集合的所有行。

当您将某些过滤条件应用于由 CROSS JOIN 生成的笛卡尔积时,结果将不再是笛卡尔积,而是与给定过滤条件匹配的笛卡尔积的子集。

所以,在你的情况下,这个查询:

SELECT 
  a.balance 
FROM account a, account b 
WHERE a.balance < b.balance

不生成笛卡尔积。

事实上,比您的查询更好的替代方法是:

SELECT 
  a.balance 
FROM account a
WHERE a.balance < (
  SELECT MAX(balance) FROM account 
)

如果您想获取余额低于最大值的所有行。

无论如何,这里使用自我CROSS JOIN 看起来很可疑。这就是为什么你最好使用子查询来代替。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多