简体   繁体   中英

Joining same table with NULL values in MySQL

I have a simple table like

user_id | meta_key | meta_value
===============================
   1    | color    | green
   2    | color    | red
   1    | size     | L
   2    | size     | L
   3    | color    | blue

...

Now I like a single query where I get all colors and sizes of a certain user

SELECT a.user_id AS ID, a.meta_value AS color, b.meta_value AS size
   FROM table AS a
   LEFT JOIN table AS b ON a.user_id = b.user_id 
WHERE a.meta_key = 'color' AND b.meta_key = 'size' GROUP BY a.meta_value, b.meta_value

This works as long I have a size for each user. If size is missing (ID 3 in example) the user isn't in the result

The predicate on b.meta_key in the WHERE is negating the "outerness" of the LEFT JOIN operation.

One option is to relocate that predicate to the ON clause, for example:

SELECT a.user_id AS ID, a.meta_value AS color, b.meta_value AS size
   FROM table AS a
   LEFT JOIN table AS b ON a.user_id = b.user_id 
      AND b.meta_key = 'size'
 WHERE a.meta_key = 'color' GROUP BY a.meta_value, b.meta_value

FOLLOWUP

Q: What if color is missing?

A: You'd need a table or inline view as a rowsource that returns every value of user_id that you want included in the result set. For example, if you had a table named "users" with one row per user_id , and you want to return every id in that table:

 SELECT u.id         AS user_id
      , a.meta_value AS color
      , b.meta_value AS size
   FROM user u
   LEFT
   JOIN `table` a
     ON a.user_id = u.id
    AND a.meta_key = 'color'
   LEFT
   JOIN `table` b
     ON b.user_id = u.id
    AND b.meta_key = 'size'
  GROUP
     BY u.id
      , a.meta_value
      , b.meta_value

We also note that the GROUP BY in your original query does not include the user_id column, so any rows that have the same meta_value for color and size will be collapsed, and you'll get only one of the user_id values that have matching color and size.

Note that if the combination of user_id and meta_key is UNIQUE (in table table ), then the GROUP BY isn't necessary.


In the normative relational model, attributes of an entity are implemented as columns. As a good example, the resultset being returned by the query looks a lot like the table we'd expect.

When an Entity Attribute Value (EAV) model is implemented in a relational database, the SQL gets an order of magnitude more complicated.


If you want to return ONLY values of user_id that have either color or size , you could get a list of user_id values with an inline view (query against table ). This query is the same as above, but replacing the reference to the user table with an inline view:

 SELECT u.id         AS user_id
      , a.meta_value AS color
      , b.meta_value AS size
   FROM ( SELECT t.user_id AS id
            FROM table t
           WHERE t.meta_key IN ('color','size')
          GROUP BY t.user_id
        ) u
   LEFT
   JOIN `table` a
     ON a.user_id = u.id
    AND a.meta_key = 'color'
   LEFT
   JOIN `table` b
     ON b.user_id = u.id
    AND b.meta_key = 'size'
  GROUP
     BY u.id
      , a.meta_value
      , b.meta_value

The where clause is killing you. Let's take b.meta_key = 'size' . If there are no rows in your B table, meta_key will come back null. The filter on that column removes them from the result. Try adding the b.meta_key = 'size' to the on clause.

尝试以下代码,

SELECT a.user_id AS ID, a.meta_value AS color, b.meta_value AS size FROM table AS a where a.meta_key = 'color' or a.meta_key = 'size'
SELECT a.user_id AS ID,
a.meta_value AS color, b.meta_value AS size
FROM table AS a
LEFT JOIN table AS b ON a.user_id = b.user_id 
WHERE a.meta_key in ('Color', 'Size')

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