Ambiguity in the SQL query


Ambiguity in the SQL query



There are two tables: table customer consists of information about customers and table payment consists of information about payments. Primary key customer_id in the customer table is a foreign key in the table payment_id. The following two queries return identical results:


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



The only difference between the queries is in the first argument in the SELECT clause: payment.customer_id vs customer.customer_id. As the customer_id is the column on which the tables are joining on, the distinction between payment.customer_id and customer.customer_id seems meaningless. However, if I try to omit the table in the query:


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



I receive



[42702] ERROR: column reference "customer_id" is ambiguous



Could you please describe where is ambiguity in the query?





How can DBMS engine know which (common) column do you prefer without any addressing to individual table(s)?
– Barbaros Özhan
Jul 1 at 7:40





6 Answers
6



Just because two columns match using the equality test, does not mean that they have the same value.



The two columns could be different types eg integer and float, or numeric etc.



Or they could be citext which does case insensitive comparisons (one table could have 'RedRum' and other 'redruM').


citext


'RedRum'


'redruM'



often the join condition might not be a strict equality (eg a network range comparison, or prefix match)



In all these cases which table you use for the result column is significant.



if you're doing an outer join table name is again significant.



Postgresql does not know when = means that the table can be implied and when it cannot, it requires it always.


=



Rule of thumb, when joining tables specify the table of every column you use in the query. that way things won't break if someone adds some columns to the other table.



You answered your own question by omitting the table in the select statement. By not specifying it, SQL doesn't know Which table is customer_id referring to.


customer_id



Could you please describe where is ambiguity in the query?



Logically there is no ambiguity in the query, as both the columns must have the same values. However, an ambiguity may appear when you use LEFT JOIN instead of INNER JOIN, e.g.:


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)



The parser just follows general rules and does not analyse a query to find out when a potential ambiguity can come to light.



It is a good practice to always prefix your column with table/subquery alias.



But in your case(only PK/FK names are shared among both tables) you could also use USING clause:


USING


SELECT
customer_id,
last_name,
amount
FROM customer
JOIN payment USING(customer_id);



DBFiddle Demo



There is also third possible solution, but I strongly recommend not to use it:


SELECT
customer_id,
last_name,
amount
FROM customer
NATURAL JOIN payment





I strongly recommend the NATURAL JOIN 'solution' because I consider it best practice to avoid duplicate columns rather than using range variables to work around the problems they cause.
– onedaywhen
Jul 2 at 12:35


NATURAL JOIN





@onedaywhen I prefer to avoid natural join, here you can find some reasons why.
– Lukasz Szozda
Jul 2 at 16:55





@onedaywhen Feel free to use any approach that suits you best. In my opinion NATURAL JOIN does not solve anything plus it could cause more problems in the future. My advice is simple: don't be lazy (use table aliases for each column and it will pay off in the long run).
– Lukasz Szozda
8 hours ago



NATURAL JOIN





First, what you call "table aliases" are actually range variables. The idea is they 'range over' the rows in the table and represent a row, not a table. Therefore, "table aliases" is most inappropriate. Second, range variables were the workaround to the problem of duplicate columns generated by pre-1992 join types. The problem was fixed in 1992 with NATURAL JOIN, a superior join type that replaces older join types...
– onedaywhen
5 hours ago



NATURAL JOIN





If I chose to use a pre-1992 join type, it would generate duplicate columns, which I'd then need to use range variables to workaround. Instead, I choose to avoid the problem entirely by using NATURAL JOIN. Your accusation of laziness is well wide of the mark.
– onedaywhen
5 hours ago


NATURAL JOIN



Legacy joins such as INNER JOIN create duplicate columns. Using INNER JOIN in your query generates two columns named customer_id. The SQL language has a workaround for this: you must prefix the column with a range variable, as others have suggested here (though using the misleading term 'table alias').


INNER JOIN


INNER JOIN


customer_id



Thankfully, the SQL language also has a fix for this problem: NATURAL JOIN creates no duplicate columns, therefore you don't need to disambiguate them:


NATURAL JOIN


SELECT
customer_id,
last_name,
amount
FROM customer
NATURAL JOIN payment



Joins that produce duplicate columns remain because nothing is ever removed from the SQL language (the "shackles of compatibility"). But you don't need any join other than NATURAL JOIN.


NATURAL JOIN



The idea is that your data element names mean the same thing throughout your data dictionary e.g. amount means one thing (pertaining to payments) and one things only (there is no amount that pertains to customers or any other type).


amount


amount



Sometimes you may need to 'project away' columns you don't want to participate in NATURAL JOIN e.g.


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



This also 'defends' your code e.g. in the unlikely event of someone adding a last_name attribute to payments.


last_name



The error means there are two columns have same name customer_id, let DB engine didn't know Which one column do you want to query.


customer_id



You need to explicitly tell the DB engine the name of the column you want to query.



Tables may be added a new column after table has been created, the new column may be the same as the old column name if you didn't clearly specify SELECT table columns of the query in the selection the will be an error on your original query.


SELECT



Here is some suggestion for you



You can give query table an Alias name, let your query Clearer.



Clearly specify SELECT table columns of the query in the selection from tables name, because of tables


SELECT



If last_name column in payment table and amount column in customer


last_name


payment


amount


customer



you can do this.


SELECT
c.customer_id,
p.last_name,
c.amount
FROM customer c
INNER JOIN payment p ON c.customer_id = p.customer_id






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

How to add background colour in existing image using Swift?

Moria Casán

How to make file upload 'Required' in Contact Form 7?