views:

88

answers:

4

I have two tables. I want to join them in a way that only one record in the right table is returned for each record in the left most table. I've included an example below. I'd like to avoid subqueries and temporary tables as the actual data is about 4M rows. I also don't care which record in the rightmost table is matched, as long as one or none is matched. Thanks!

table users:

-------------
| id | name |
-------------
| 1  | mike |
| 2  | john |
| 3  | bill |
-------------

table transactions:

---------------
| uid | spent | 
---------------
| 1   | 5.00  |
| 1   | 5.00  |
| 2   | 5.00  |
| 3   | 5.00  |
| 3   | 10.00 |
---------------

expected output:

---------------------
| id | name | spent |
---------------------
| 1  | mike | 5.00  |
| 2  | john | 5.00  |
| 3  | bill | 5.00  |
---------------------
+2  A: 

Use:

  SELECT u.id,
         u.name,
         MIN(t.spent) AS spent
    FROM USERS u
    JOIN TRANSACTIONS t ON t.uid = u.id
GROUP BY u.id, u.name

Mind that this will only return users who have at least one TRANSACTIONS record. If you want to see users who don't have supporting records as well as those who do - use:

   SELECT u.id,
          u.name,
          COALESCE(MIN(t.spent), 0) AS spent
     FROM USERS u
LEFT JOIN TRANSACTIONS t ON t.uid = u.id
 GROUP BY u.id, u.name
OMG Ponies
+1 nice, I was struggling to come up with a solution
northpole
Ah! Of course. Sometimes I get so myopic. One question aside, wasn't there a JOIN in earlier versions of MySQL that naturally did this?
Mike Sherov
Yes, it is called a left join. :) Right join will force a match. Left joins will include matches but not require them.
TheJacobTaylor
@Mike Sherov: ANSI-92 has a NATURAL JOIN, supported by [MySQL since 3.23](http://dev.mysql.com/doc/refman/4.1/en/join.html), but it's just an inner join. I don't know how early you're thinking...
OMG Ponies
A: 

If you do not care about the particular row that you get back.

select id, name, spent
from users
left join transactions on users.id = transactions.uid
group by id

This will return one row per user. It will be the first matched transaction.

Cheers, Jacob

TheJacobTaylor
A: 

My apologies if this doesn't actually answer your question. It looks like you are trying to see which users have at least one transaction. You could do this and in the process see how much each user has spent by doing something like this:

SELECT 
    u.id,
    u.name,
    SUM(t.spent) AS total
FROM 
    USERS u
    INNER JOIN TRANSACTIONS t 
        ON t.uid = u.id
GROUP BY 
    u.id
    , u.name
wshato
They specifically state in the question one or no matches are both fine.
TheJacobTaylor
Oops, my mistake. I read it as "one or more"
wshato
A: 

See SO 3305709 for an equivalent recent question, with a number of reasonable answers. The DBMS cited there is Postgres, but the differences here are negligible.

Jonathan Leffler
`DISTINCT ON` is only supported by Postgres, and DISTINCT would return multiple rows with distinct `spent` values per user.
OMG Ponies