简体   繁体   English

MySQL联接:从子表中选择一行满足条件,否则为NULL

[英]MySQL JOINS: Select one row from child table is condition is met, otherwise NULL

I am trying to join two tables but I am having some trouble. 我试图加入两个表,但是遇到了一些麻烦。 This is the main table, packs 这是主表, packs

这是主表

And here is the second table, media 这是第二张桌子, media 在此处输入图片说明

The media is for storing images of packs . media用于存储packs图像。 Each pack can have 0 or more images. 每个包可以包含0个或更多图像。 When the pack has images, one of them will have the is_default field set to 1 to indicate the main image to show. 当包中有图像时,其中之一将is_default字段设置为1以指示要显示的主图像。

I want to get all pack and one image for each. 我想获取所有包装和一张图片。 If there are no images, then just a simple NULL , otherwise get the image that is is_default . 如果没有图像,则仅是一个简单的NULL ,否则获取is_default的图像。

Here is my query. 这是我的查询。

SELECT 
    pack.*,
    ( SELECT media.src FROM packs pack LEFT JOIN packs_media media ON pack.id = media.pack_id WHERE media.is_default = 1 GROUP BY media.id LIMIT 1 ) AS image 
FROM packs pack 
LEFT JOIN packs_media ON media.pack_id = pack.id 
GROUP BY pack.id 
ORDER BY pack.id DESC

I have a total of three packs, and only one of them has some images. 我一共有三包,其中只有一包有一些图片。 The query returns 3 results. 该查询返回3个结果。 The problem is that all three results have the same image , when only one should and the other two should have the image field null/empty. 问题是,当只有一个结果应具有相同的image时,所有三个结果应具有相同的image ,而其他两个结果的image字段应为空/空。

Is there any way of doing this with one query only ? 有什么办法只对一个查询执行此操作? I want to avoid querying the media table in a loop. 我想避免循环查询media表。

Thank you 谢谢

You need a correlated subquery rather than a join: 您需要一个相关的子查询而不是联接:

SELECT p.*,
      (SELECT m.src
       FROM packs_media m
       WHERE p.id = media.p 
       ORDER BY m.is_default DESC, m.id
       LIMIT 1
      ) AS image 
FROM packs p 
ORDER BY p.id DESC;

In fact, from the description of the problem, no JOIN is necessary in the outer query either. 实际上,从问题的描述来看,外部查询中也不需要JOIN So the GROUP BY is not needed either. 因此,也不需要GROUP BY

Note the change to the ORDER BY clause. 注意对ORDER BY子句的更改。 This guarantees that the default value is chosen first. 这样可以确保首先选择默认值。

And, your query gets the same value for all rows because the subquery is independent of the outer query. 并且,由于子查询独立于外部查询,因此查询对所有行都具有相同的值。 So, the same value is always chosen. 因此,始终选择相同的值。

I had a similar problem lately, and it has a really-really sweet solution, to be honest. 我最近也遇到过类似的问题,说实话,它确实有一个非常好的解决方案。 At first glance, an obvious try would be adding the WHERE condition is_default = 1 . 乍一看,显而易见的尝试是添加WHERE条件is_default = 1 However, if we do this, where there are no images, the condition won't be met, meaning where it should return NULL , it just skips that row. 但是,如果执行此操作,则在没有图像的情况下将不满足条件,这意味着它应返回NULL ,而只是跳过该行。

However, if you add this condition to the LEFT JOIN part, as I did, it works like a charm. 但是,如果像我一样将此条件添加到“ LEFT JOIN部分中,它的工作原理就像一种魅力。 It's beacause if the LEFT JOIN conditions met, it returns the image, and if it doesn't, it returns NULL , per the definition of LEFT JOIN . 它东阳如果LEFT JOIN条件满足,它返回的形象,如果没有,则返回NULL ,每定义LEFT JOIN

So the result should be: 因此结果应为:

SELECT pack.*, media.src
FROM packs pack 
LEFT JOIN media
ON media.pack_id = pack.id
AND media.is_default = 1

And I think you shouldn't add GROUP BY it just messes things up. 而且我认为您不应该添加GROUP BY只会使事情搞砸。

If you want to solve your problem even when there's no is_default ... well, that is a harder nut to crack, but I think I have a solution which won't dwell into long lines of conditions and subqueries. 如果即使在没有is_default情况下也想解决您的问题,那么,这是一个很难解决的问题,但是我认为我有一个解决方案,不会涉及很多条件和子查询。 Sadly, I have the feeling that this problem can't be solved without a subquery. 可悲的是,我感到没有子查询无法解决此问题。 My idea was not to choose the item whose media.is_default equals to 1 , rather which has the highest media.is_default . 我的想法不是选择media.is_default等于1的项目,而是选择media.is_default最高的media.is_default As follows: 如下:

SELECT pack.*, media.src
FROM packs pack 
LEFT JOIN media
ON media.pack_id = pack.id
AND media.id = 
   (SELECT m.id FROM media m
    WHERE m.pack_id = media.pack_id
    ORDER BY m.is_default DESC
    LIMIT 1
   )

EXPLANATION: This is how it works: we order all by is_default in a descending order, and refer back to the outer query to use only the current pack_id (I hope it works this way.) If there is a 1, it obviously will be the first item, otherwise it will be a random item, most likely the first in order of ID. 解释:这是这样工作的:我们按is_default所有顺序降序排列,然后返回外部查询以仅使用当前的pack_id (我希望它以这种方式工作。)如果有1,则显然是第一个项目,否则将是一个随机项目,最有可能是ID的第一个项目。 And you just take that element and use it. 您只需使用该元素并使用它。 I cannot guarantee if it works at all. 我不能保证它是否完全有效。 Try using IN instead of = as a possible bug fix. 尝试使用IN代替=作为可能的错误修复。

However, I would NOT advise this solution. 但是,我建议这种解决方案。 I'd rather change the previous logic to avoid this problem (eg when the user adds a media to an empty pack, is_default = 1 on that picture). 我宁愿更改以前的逻辑来避免此问题(例如,当用户将媒体添加到空包时,该图片上的is_default = 1 )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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