我被问到以下问题:
编写一条 SQL 语句对 salesman、customer 和orders 表进行联接,其形式是每个表的相同列只出现一次,并且只出现相关行。
我执行了以下查询:
SELECT * FROM orders NATURAL JOIN customer NATURAL JOIN salesman;
然而,我没想到会出现以下结果:
我的疑问在于步骤2。
为什么我没有获得 salesman_id 5002、5003 和 5007 的行?
我知道自然连接使用公共列来完成行。
此处所有 Salesman_ids 都出现在步骤 1 的结果中。
为什么最终结果不等于步骤 1 中添加了来自销售人员的非重复列的表?
...每个表的同一列都会出现一次
是的
Natural Join
就是这样。
...并且只有关系行才会出现。
我不知道这意味着什么。
我不同意那些人的说法:不要使用
Natural Join
。但毫无疑问,如果您计划使用 Natural Join
进行查询,则必须设计架构,以便(宽松地说)“相同的列名意味着相同的事情”。
那么这个练习将告诉您具有不同含义的同名列的危险。这种危险有时被称为“连接陷阱”或“连接陷阱”。 (这并不是真正的陷阱:您只需要学习在设计不良的模式上编写查询的方法。)
更准确的说法是:如果两个不同表中的列名称相同,则该列必须是其中至少一个的键。所以:
city
不是任何这些表中的键,
所以不应该在 Natural Join
中被“捕获”。salesman_id
不是表 customer
中的键,
因此不应在表连接中“捕获”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, ...
。
为什么最终结果不等于步骤 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] ...
自然连接的定义是,如果每个输入保存使其谓词成为 true 语句的行,那么它们的自然连接将保存使这些谓词的 AND/连接成为 true 语句的行。所以(你的问题):
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 的行?
因为这些推销员不在他们的客户居住的城市工作。
编写SQL语句连接表salesman、customer和orders,以便每个表的相同列出现一次并且仅返回关系行。但它在 SQL Server 中是如何工作的 如果我编写查询“SELECT * FROM ORDERS JOIN SALESMAN ON ORDERS.salesman_id = SALESMAN.salesman_id JOIN CUSTOMER ON ORDERS.cut_id = CUSTOMER.cut_id;” 这会显示重复的列