简体   繁体   中英

Left outer join with multiple matches, how to return a specific one?

I have a problem which I think might be solved with proper use of left outer join, but I'm unable to construct suitable query. OTOH, there may also be some other, more clever solution with SQL. In addition, this could easily be solved with some programming, but I want to avoid that and find as "clean" solution as possible.

Background: let's say I'm creating a website that lists some car brands and the user can select which ones he owns/has owned (I'm not really doing that, but this example illustrates the point). In addition, for the selected ones he can optionally enter some additional info about them, eg year and some free text like specific model, comments or whatever. In addition, the information entered is stored in a relational database (MySQL in my case) and the user can retrieve and change his answers later.

Let's say there are two database tables:

BRAND
------------
ID INT
NAME VARCHAR(50)

OWNED
------------
ID INT
BRAND_ID INT
OWNER_ID INT
YEAR INT
COMMENT VARCHAR(100)

(here BRAND_ID + OWNER_ID is an unique index, so there can be only one row, and thus one year & comment for each BRAND/OWNER combination)

The data in these tables may look something like this:

BRAND
--------------
ID | NAME
--------------
1  | Cadillac
2  | Chevrolet
3  | Dodge
4  | Ford

OWNED
-----------------------------------------
ID | BRAND_ID | OWNER_ID | YEAR | COMMENT
-----------------------------------------
1  | 1        | 1        | null | 70's Fleetwood
2  | 2        | 1        | 2000 | Crappy car
3  | 2        | 2        | null | I really liked it
4  | 4        | 2        | 1999 | null

Now, to facilitate easy creation of the web page, what I would like to do is to with one SELECT display all brands in table BRAND, and for each BRAND to know whether current user has owned it or not, and if he has, also list his year and comment (if any). In other words, something like this (assuming current user is 2):

NAME      | OWNER_ID | YEAR | COMMENT
-------------------------------------
Cadillac  | null     | null | null
Chevrolet | 2        | null | I really liked it
Dodge     | null     | null | null
Ford      | 2        | 1999 | null

I tried doing something like:

select NAME, OWNER_ID, YEAR, COMMENT from BRAND left join OWNED on 
BRAND.ID = OWNED.BRAND_ID where OWNER_ID = 2 or OWNER_ID = null

but that fails because 1 owns a Cadillac and thus Cadillac is left from the result. OTOH if I omit the where clause, I will get two rows for Chevrolet, which is also not desirable.

So, if there is a clean solution with SQL (either with or without left outer join), I'd like to know how to do it?

I am guessing you want this:

select NAME, OWNER_ID, YEAR, COMMENT
from BRAND left join
     OWNED o
     on BRAND.ID = OWNED.BRAND_ID and OWNER_ID = 2 ;

Seems like what you might actually want is a list of the owners, followed by what they owned and the details. You can that by adjusting the owner id at the bottom of this one:

SELECT owned.owner_id, brand.id, brand.name, owned.year, owned.comment 
FROM owned
INNER JOIN brand
ON owned.brand_id = brand.id
WHERE owned.owner_id = 2

Tested here: http://sqlfiddle.com/#!9/5b52ba

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM