有两个表:表customer
包含有关客户的信息,表payment
包含有关付款的信息。 customer_id
表中的主键customer
是表payment_id
中的外键。以下两个查询返回相同的结果:
SELECT
payment.customer_id,
last name,
amount
FROM customer
INNER JOIN payment ON customer.customer_id = payment.customer_id
SELECT
customer.customer_id,
last_name,
amount
FROM customer
INNER JOIN payment ON customer.customer_id = payment.customer_id
查询之间的唯一区别在于SELECT
子句中的第一个参数:payment.customer_id
与customer.customer_id
。由于customer_id
是连接表的列,因此payment.customer_id
和customer.customer_id
之间的区别似乎毫无意义。但是,如果我尝试在查询中省略该表:
SELECT
customer_id,
last_name,
amount
FROM customer
INNER JOIN payment ON customer.customer_id = payment.customer_id
我收到
[42702]错误:列引用“ customer_id”不明确
能否请您描述一下查询中的歧义?
答案 0 :(得分:2)
您通过省略select语句中的表来回答自己的问题。通过不指定它,SQL不知道customer_id
引用哪个表。
答案 1 :(得分:2)
能否请您描述一下查询中的歧义?
从逻辑上讲,查询中没有歧义,因为两列必须具有相同的值。但是,当您使用LEFT JOIN
而不是INNER JOIN
时可能会出现歧义,例如:
INSERT INTO customer (customer_id, last_name) VALUES
(1, 'Smith'),
(2, 'Jones');
INSERT INTO payment (customer_id, amount) VALUES
(1, 100);
SELECT
customer.customer_id,
payment.customer_id,
last_name,
amount
FROM customer
LEFT JOIN payment ON customer.customer_id = payment.customer_id
customer_id | customer_id | last_name | amount
-------------+-------------+-----------+--------
1 | 1 | Smith | 100
2 | | Jones |
(2 rows)
解析器仅遵循一般规则,并且不分析查询来找出何时可能出现歧义。
答案 2 :(得分:2)
仅仅因为两列使用相等性测试匹配,并不意味着它们具有相同的值。
这两列可以是不同的类型,例如整数和浮点数或数字等。
或者它们可以是citext
which does case insensitive comparisons(一个表可以包含'RedRum'
,另外一个'redruM'
)。
通常加入条件可能不是严格相等的(例如,网络范围比较或前缀匹配)
在所有这些情况下,您用于结果列的哪个表都很重要。
如果您正在执行外部联接表,则名称再次有意义。
Postgresql不知道何时=
意味着可以隐含该表,何时不可以隐含该表,则始终需要它。
经验法则,在联接表时,请指定您在查询中使用的每一列的表。这样,如果有人在其他表中添加一些列,事情就不会中断。
答案 3 :(得分:1)
该错误表示有两列具有相同的名称customer_id
,让数据库引擎不知道您要查询哪一列。
您需要明确告知数据库引擎要查询的列的名称。
在创建表之后,可以在表中添加一个新列,如果未在选择遗嘱中明确指定查询的SELECT
个表列,则新列可能与旧列名相同。是原始查询的错误。
这是给你的一些建议
您可以给查询表一个别名,让查询更清晰。
由于表的原因,在表名的选择中明确指定查询的SELECT
个表列
如果last_name
表中的payment
列和amount
中的customer
列
您可以这样做。
SELECT
c.customer_id,
p.last_name,
c.amount
FROM customer c
INNER JOIN payment p ON c.customer_id = p.customer_id
答案 4 :(得分:1)
一个好习惯是始终为表/子查询别名添加列前缀。
但是在您的情况下(两个表之间仅共享PK / FK名称),您也可以使用USING
子句:
SELECT
customer_id,
last_name,
amount
FROM customer
JOIN payment USING(customer_id);
还有第三个可能的解决方案,但我强烈建议不要使用它:
SELECT
customer_id,
last_name,
amount
FROM customer
NATURAL JOIN payment
答案 5 :(得分:1)
旧式联接(例如INNER JOIN
)会创建重复的列。在查询中使用INNER JOIN
会生成两个名为customer_id
的列。 SQL语言对此有一种解决方法:您必须像其他人在这里建议的那样,在列的前面加上范围变量(尽管使用了误导性的术语“表别名”)。
非常感谢,SQL语言也解决了该问题:NATURAL JOIN
没有创建重复的列,因此您无需消除歧义:
SELECT
customer_id,
last_name,
amount
FROM customer
NATURAL JOIN payment
保留产生重复列的联接,因为从未从SQL语言中删除任何内容(“兼容性”)。但是除了NATURAL JOIN
之外,您不需要其他任何连接。
这个想法是,您的数据元素名称在整个数据字典中都具有相同的含义,例如amount
意味着一件事(与付款有关),仅意味着一件事(没有与客户或任何其他类型有关的amount
)。
有时候,您可能需要“投影”您不想参与的NATURAL JOIN
列,例如
WITH
C AS ( SELECT customer_id, last_name FROM customer ),
P AS ( SELECT customer_id, amount FROM payment )
SELECT
customer_id,
last_name,
amount
FROM C
NATURAL JOIN P
这也可以“保护”您的代码,例如如果有人在付款中添加了last_name
属性,则可能性很小。