简体   繁体   中英

MySQL JOIN: Select a row if a bool column is 1, otherwise select the first row

I have two tables: products(id) and product_images(id, parent_id, src, is_default)

Each product has n images.

I want to get all products and one image for each of them.

If the column is_default is 1( BOOL ), then get that image, otherwise get the first one.

My query looks like this

SELECT 
   main.*,
   images.id image_id, images.src image_src
FROM products main 
   LEFT JOIN product_images images ON main.id = images.parent_id AND images.is_default = 1
GROUP BY main.id
ORDER BY main.id DESC

Obviously the JOIN condition is not right and I can't figure it out.

Hope you can help,

Thank you.

I suppose that the first image is the one with the lowest ID, I would start with this query:

SELECT
  parent_id,
  COALESCE(
    MIN(CASE WHEN is_default=1 THEN ID END),
    MIN(id)
  ) AS image_id
FROM product_images
GROUP BY parent_id

this will return the image where is_default = 1, if there's no default MIN(case when...) will be null and then COALESCE will select just MIN(id). Then you can join your query with the previous subquery:

SELECT main.*, images.id image_id, images.src image_src
FROM
  products main LEFT JOIN JOIN (
    SELECT
      parent_id,
      COALESCE(
        MIN(CASE WHEN is_default=1 THEN ID END),
        MIN(id)
      ) AS min_image_id
    FROM product_images
    GROUP BY parent_id
  ) fi ON fi.parent_id = main.id
  LEFT JOIN product_images images
  ON images.id = fi.min_image_id

Using GROUP_CONCAT.

This gets all the image details for each main ordered by is_default descending, and then by id. So if there are no records with is_default set to 1 then it will get the first image first. Then uses SUBSTRING_INDEX to get the first details.

Note that if images.src can contain a comma you might want to use a different delimiter.

SELECT 
   main.*,
   SUBSTRING_INDEX(GROUP_CONCAT(images.id ORDER BY images.is_default DESC, images.id), ',', 1) AS image_id, 
   SUBSTRING_INDEX(GROUP_CONCAT(images.src ORDER BY images.is_default DESC, images.id), ',', 1) AS image_src
FROM products main 
LEFT JOIN product_images images ON main.id = images.parent_id 
GROUP BY main.id
ORDER BY main.id DESC

You can grab the single image information in a subquery:

SELECT main.*, 
 (SELECT images.id 
  FROM product_images image 
  WHERE main.id = images.parent_id 
  AND images.is_default = 1
  ORDER BY images.id
 ) image_id,
 (SELECT images.src 
  FROM product_images image 
  WHERE main.id = images.parent_id 
  AND images.is_default = 1
  ORDER BY images.id
 ) image_src
FROM products main

You will need a sub-query. I think this is the simplest solution.

SELECT
    *
FROM (
    SELECT main.*, images.id image_id, images.src image_src
    FROM products as main
    LEFT JOIN product_images as images ON main.id = images.parent_id
    ORDER BY images.is_default DESC
) as t
GROUP BY t.id

This does what you want, but orders by the images.is_default desc, so that if the is_default is 1, then this will show at the top. The outer query GROUP BY will force the one with the is_default to be selected if is_default is set.

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