Consider the following relation from the Bars and Beers database:
sells (bar, beer, price): indicates the price of each beer sold at each bar (note that each bar can sell many beers and many bars can sell the same beer, at possibly different prices).
The following query finds the name of the minimum price beer that can be found at any bar.
SELECT s.beer
FROM sells s
WHERE NOT EXISTS (SELECT si.beer
FROM sells si
WHERE si.price <= s.price)
Hi, I am learning sql slowly and I am having trouble understanding the part “si.price <= s.price”. Wouldn’t the entire query always return an empty table since the nested select is basically comparing the same two tables?
There is a bug in that query – it will never return any rows, because no price is not equal to itself.
To work, it should be:
Assuming that change is made, the explanation of how it works s that the inner query executes once for every row in the table and depends on the outer table (called a “co-related subquery”). Because the same table is used in the inner query, you need to alias the outer table (as “s” in this case). The inner query asserts that there is no (other) price lower than the price in the row being executed.
Incidentally, aliasing the inner query is unnecessary – you could remove “s1” and it would still work, because the inner query is the default scope:
Further, this is a “bad” query because of the corelated subquery, which causes “n” queries to execute (one for each row). You’re better off using the
min()aggregate function:Apart from this query being simpler and easier to understand (two good things), it executes faster because only two queries are executed (one subquery to find the minimum price, and one to get the whole row that has it).
Finally, the last (albeit minor) bug is that all of these will return multiple rows if there are multiple low-priced beers with the same price. To return the lowest price beer, you would need to limit the number of rows to 1 and decide how to break the tie (perhaps on name). In fact, returning just one row allows for an even simpler query:
And if you needed to consistently break a tie (ie not return a random same-priced beer with the same lowest price), also order by the name: