-1

我被问到以下问题:

编写一个 SQL 语句,对表 salesman、customer 和 orders 进行连接,使每个表的同一列出现一次,并且只出现关系行。

我执行了以下查询:

SELECT * FROM orders NATURAL JOIN customer NATURAL JOIN salesman;

但是,我没想到会出现以下结果:

在此处输入图像描述

在此处输入图像描述

我的疑问在于第 2 步。

为什么我没有得到 salesman_id 5002、5003 和 5007 的行?

我知道自然连接使用公共列来完成行。

这里所有的 Salesman_ids 都出现在第 1 步的结果中。

为什么最终结果不等于第 1 步生成的表,其中添加了来自推销员的非重复列?

4

2 回答 2

1

...每个表的同一列将出现一次

Natural Join的。

...并且只有关系行会出现。

我不知道那是什么意思。

我不同意那些说:不要使用Natural Join. 但是,如果您打算将其Natural Join用于查询,那么您必须设计架构以便(松散地说)“相同的列名意味着相同的事情”,这当然是真的。

然后这个练习将教你使用同名列但含义不同的危险。这种危险有时被称为“连接陷阱”或“连接陷阱”。(并不是一个真正的陷阱:您只需要学习在设计不佳的模式上编写查询的方法。)

更准确的说法是:如果您在两个不同的表中有同名的列,则该列必须是其中至少一个的键。所以:

  • city不是任何这些表中的键,因此不应在Natural Join.
  • salesman_id不是 table 中的键customer,因此不应在 table 的连接中“捕获” orders

修复此查询的主要方法是重命名某些列以避免“捕获”(见下文)。还值得一提的是,一些 SQL 方言允许:

SELECT *
FROM orders
NATURAL JOIN customer ON customer_id
...

ON column(s)句话的意思是:验证两个表之间唯一的共同列是那些命名的。否则拒绝查询。所以你的查询将被拒绝。

重命名意味着你不应该使用SELECT *. (无论如何,这对于“生产代码”是危险的,因为每次架构更改时您的查询可能会产生不同的列。)解决这个问题的最简单方法可能是为您的三个基表创建三个视图,“意外”相同 -给定其他名称的命名列。对于这一查询:

SELECT ord_no, purch_amt, ord_date, customer_id,
       salesman_id AS order_salesman_id
FROM orders
NATURAL JOIN (SELECT customer_id, cust_name,
                     city AS cust_city, grade,
                     salesman_id AS cust_salesman_id
              FROM customer) AS customer_grr
NATURAL JOIN (SELECT salesman_id, name,
                     city AS salesman_city,
                     commission
              FROM salesman) AS salesman_grr

我正在使用显式AS来显示重命名。大多数 SQL 方言允许您省略该关键字;就放city cust_city, ...

于 2017-07-27T01:21:29.997 回答
0

为什么最终结果不等于带有 [...] 的第 1 步产生的表格?

因为自然连接不会按照您的预期工作——无论如何,因为您没有说。

在关系代数方面: 自然连接返回行
• 其列集是输入列集的并集并且
• 在两个输入中都有一个子行。

在商业术语中:每个表和查询结果都包含使某些语句模板的行——它的(特征)谓词——它的“含义”——成为一个真实的语句。设计器为基表提供谓词。在这里,类似:

Orders = rows where
    order [ord_no] ... and was sold by salesman [salesman_id] to customer [customer_id] 
Customer = rows where
    customer [customer_id] has name [cust_name] and lives in city [city]
        and ... and is served by salesman [salesman_id]
Salesman = rows where
    salesman [salesman_id] has name [name] and works in city [city] ...

自然连接的定义是,如果每个输入都包含使其谓词成为真语句的行,那么它们的自然连接会保存使这些谓词的 AND/连接成为真语句的行。所以(您的查询):

Orders natural join Customer natural join Salesman = rows where
    order [ord_no] ... and was sold by salesman [salesman_id] to customer [customer_id] 
and customer [customer_id] has name [cust_name] and lives in city [city]
        and ... and is served by salesman [salesman_id]
and salesman [salesman_id] has name [name] and works in city [city] ...

因此,自然联接要求查询客户居住在推销员所在城市的行。如果这不是您想要的,那么您不应该使用该表达式。

请注意表的自然连接的含义如何是其输入表含义的(简单)函数。对于所有关系运算符都是如此。因此,每个查询表达式都有一个含义,由其基表含义和关系运算符构建而成。
是否有任何经验法则可以根据人类可读的描述构造 SQL 查询?

为什么我没有得到 salesman_id 5002、5003 和 5007 的行?

因为这些推销员不在他们的客户居住的城市工作。

于 2017-10-23T03:06:54.910 回答