多次使用LEFT JOIN只能使用1行

时间:2012-05-01 05:18:41

标签: mysql sql join left-join outer-join

这是一个智能标签基础图像搜索系统。用户在这样的表格中添加带有适当标签的图像:

image (id, title, ...)
tag (id, title) /* It doesn't matter who has created the tag*/
imagetag (image_id, tag_id) /* One image may have multiple tags */

用户查看图片以及{这些图片标记 * 的访问记录在usertagview表中。 (请注意,我为此目的使用了INSERT ON DUPLICATE UPDATE查询。)

usertagview (user_id, tag_id, view_count)

现在请考虑一些带有以下标签的图片:

  • riverday(这是一张照片,显示阳光灿烂的日子里的河流)
  • rivernight(在午夜月光下的那条河)
  • treeday
  • treenight
  • flowerday
  • flowernight

用户搜索标记river,并显示标记为 river 的所有图片:在这种情况下,第一张图片(标记为河日)并显示第二个(由河夜标记)。用户观看次数第二张图片(由rivernight标记)并查看表格usertagview

然后,用户尝试对标记tree进行新搜索,并查看tree night图片。

我希望如果用户搜索flower,则flower night优先于flower day。我的意思是flower night应该出现在flower day之前。换句话说,我想要一个查询,根据用户之前的视图列出flower标记的图像。 ( flower night首先,其他flower下一个)。

我的查询失败了:

SELECT

    DISTINCT (image.id) AS image_id,
    image.title AS image_title,
    SUM(usertagview.view_count) AS SUM_of_all_tag_views_for_each_image

FROM (image)

JOIN imagetag ON imagetag.image_id = image.id

**LEFT JOIN** usertagview ON
    usertagview.tag_id = imagetag.tag_id
    AND usertagview.user_id = {$user_id_from_php}

WHERE

    imagetag.tag_id IN ( {impolde(',', $array_of_id_of_tags_that_the_user_has_entered)} )
    AND
    usertagview.tag_id IN
        (SELECT tag_id FROM imagetag WHERE userimagetag.image_id = image.id)

ORDER BY SUM_of_all_tag_views_for_each_image DESC

问题

我的查询中的 **LEFT JOIN** 与普通 INNER JOIN 没有区别。它们都有相同的结果。即使我使用 RIGHT JOIN ,它也没有区别。

3 个答案:

答案 0 :(得分:4)

您的left join行为与inner join相同的原因是因为您的left join子句中的where有其他条件。这基本上会将您的outer join变为inner join

这样做的原因是,如果usertagview.tag_id在没有匹配记录的情况下为NULL,则IN子句中的WHERE语句将删除该行NULL值。

要解决此问题,您可以将usertagview.tag_id IN ...支票移入加入的ON条款中。

然而,这只是你问题的一半。您只检查用户输入的特定标记的视图,但如果我了解您的实际要求,则需要检查与任何标记相关联的标记的视图,该标记的标记与您的搜索字词匹配

例如,当用户输入flower时,您希望首先找到标有flower的图像,然后检查该组图像的所有其他标记的视图。

我相信以下查询可以完成此操作,this SQL Fiddle shows the query in action

SELECT
  i.id AS image_id,
  i.title AS image_title,
  IFNULL(SUM(utv.view_count), 0) AS associated_view_totals
FROM
  imagetag originalTag
  JOIN imagetag associatedTags 
    ON associatedTags.image_id = originalTag.image_id
  JOIN image i 
    ON i.id = associatedTags.image_id
  LEFT JOIN usertagview utv 
    ON utv.user_id = 1
    AND utv.tag_id = associatedTags.tag_id
WHERE
  -- User searches for flower tag (Let's assume 5 == flower)...
  originalTag.tag_id IN (5)
GROUP BY
  i.id,
  i.title
ORDER BY 
  associated_view_totals DESC

答案 1 :(得分:3)

这是一个常见问题。幸运的是,这很容易解决。

看到了吗?

LEFT JOIN usertagview ON
    usertagview.tag_id = imagetag.tag_id -- see this?
    AND usertagview.user_id = {$user_id_from_php}  

WHERE

    imagetag.tag_id IN ( {impolde(',', $array_of_id_of_tags_that_the_user_has_entered)} )
    AND

这个?

    usertagview.tag_id IN -- and this?
        (SELECT tag_id FROM imagetag WHERE userimagetag.image_id = image.id)

两个条件共享相同的字段,即usertagview.tag_id。 因此,WHERE子句上的usertagview.tag_id IN (SELECT tag_id FROM ...)基本上取消了在LEFT JOIN图像标签上使用的任何成功usertagview。

因此,要修复查询,请将INNER JOIN - y usertagview恢复为LEFT JOIN,然后将usertagview条件移至JOIN条件:

SELECT

    DISTINCT (image.id) AS image_id,
    image.title AS image_title,
    SUM(usertagview.view_count) AS SUM_of_all_tag_views_for_each_image

FROM (image)

JOIN imagetag ON imagetag.image_id = image.id

LEFT JOIN usertagview ON
    usertagview.tag_id = imagetag.tag_id
    AND usertagview.user_id = {$user_id_from_php}

    -- moved the WHERE condition here
    AND
    usertagview.tag_id IN
        (SELECT tag_id FROM imagetag WHERE userimagetag.image_id = image.id)


WHERE

    imagetag.tag_id IN ( {impolde(',', $array_of_id_of_tags_that_the_user_has_entered)} )

ORDER BY SUM_of_all_tag_views_for_each_image DESC

那会解决它。如果不是(因为我不确切地知道你的桌子彼此是一对多的,或者彼此一对一,所以在这种情况下我会抛出什么通常有效),尝试将INNER JOIN imagetag更改为LEFT JOIN。由于WHERE子句中的imagetag条件将取消LEFT JOIN条件产生的任何行,因此将imagetag条件从WHERE子句移至LEFT JOIN as好吧:

SELECT

    DISTINCT (image.id) AS image_id,
    image.title AS image_title,
    SUM(usertagview.view_count) AS SUM_of_all_tag_views_for_each_image

FROM (image)

LEFT JOIN imagetag ON imagetag.image_id = image.id
    -- WHERE clause condition moved here.
    -- WHERE conditionXXX basically cancels out whatever rows
    -- obtained from `LEFT JOIN ON conditionXXX`, in which conditionXXX share
    -- the same field.  IN this case, it is imagetag.
    AND    
    imagetag.tag_id IN ( {impolde(',', $array_of_id_of_tags_that_the_user_has_entered)} )


LEFT JOIN usertagview ON
    usertagview.tag_id = imagetag.tag_id
    AND usertagview.user_id = {$user_id_from_php}

 -- moved here
    AND
    usertagview.tag_id IN
        (SELECT tag_id FROM imagetag WHERE userimagetag.image_id = image.id)



ORDER BY SUM_of_all_tag_views_for_each_image DESC

如果第二个建议仍无法提供结果,那么您的查询目前正在处理多个一对多表关系。如果在查询中有多个一对多表关系,SQL无法弄清楚您的意图;在这种情况下,您需要展平结果以获得正确的输出。以下是关于如何展平结果的一个很好的演练:http://www.anicehumble.com/2012/05/sql-count-computer-program-does-what.html

答案 2 :(得分:1)

你总是得到你的桌面图像+ INNER JOIN imagetag的结果,没有你加入的下一个/之后的内容。如果你想要更多的结果,你需要LEFT JOIN imagetag表。