左外连接缺失记录

时间:2012-07-24 18:31:45

标签: mysql left-join

我正在从几个表中选择数据,但主要想法是产品可能有也可能没有与之相关的折扣记录,无论是折扣百分比还是美元折扣。我正在使用左外连接(可能不正确)并且返回相同的美元和百分比值,无论记录是否存在。

查询类似于:

SELECT Items.ItemID, Items.name, Items.price, 
       ItemDiscounts.percentOff, ItemDiscounts.dollarOff, 
       ItemAttributes.ColorName, ItemStuff.StuffID
FROM Items, ItemAttributes, ItemStuff
LEFT OUTER JOIN ItemDiscounts
  ON ItemDiscounts.ItemID = ItemID
  AND (
    ItemDiscounts.percentOff > 0
    OR ItemDiscounts.dollarOff > 0
  )
WHERE Items.ItemID = ItemAttributes.ItemID
AND ItemStuff.ItemID = Items.ItemID
GROUP BY ItemStuff.StuffID

奇怪的是,在所有结果中,percentOff返回“1”,dollarOff返回“0”,无论每个项目是否都有自己的相关折扣记录。对于吐痰,我将ItemDiscounts.percentOff > 0更改为ItemDiscounts.percentOff > 1,然后dollarAmount更改为全部2,percentOff全部为0。

我对此感到有些困惑,所以任何帮助都会受到赞赏。

3 个答案:

答案 0 :(得分:1)

你的ON子句中有一个对ItemID的非限定引用......不清楚为什么不引发“模糊列”异常。 (显然,它对MySQL来说并不含糊,并且MySQL正在确定哪个ItemId被引用,它的概率很好,而不是你想要的那个。

此外,您的查询包含对ItemStuff行源的引用,但查询中没有显示此类行为。

我还怀疑GROUP BY的行为会给你一个不符合你期望的结果集。 (很可能,现在,它正在掩盖查询中的真正问题,这可能是您不想要的CROSS JOIN操作。

我建议您在没有GROUP BY子句的情况下尝试查询,并确认结果集是您期望缺少GROUP BY子句的结果。

注意:大多数其他关系数据库引擎会抛出一个与您在查询中显示的GROUP BY异常。它们(基本上)要求SELECT列表中的每个非聚合都包含在GROUP BY中。你可以让MySQL以相同的方式运行(使用sql_mode的一些特定设置。)MySQL更自由,但你得到的结果集可能不符合你的期望。

注意:我没有看到此查询如何传递语义检查,并且在给定对不存在的ItemStuff行源的引用时,根本不返回任何结果集。


为了提高可读性,我建议您不要使用逗号作为连接运算符,而是使用JOIN关键字。我还建议您将连接谓词从WHERE子句移动到ON子句。我也更喜欢为每个行源提供一个别名,并使用该别名来限定它的列。

鉴于您在查询中显示的内容,我会写(我能理解的部分),如下所示:

SELECT i.ItemID
     , i.name
     , i.price
     , d.percentOff
     , d.dollarOff
     , a.ColorName
  FROM Items i 
  JOIN ItemAttributes a
    ON a.ItemID = i.ItemID
  LEFT 
  JOIN ItemDiscounts d
    ON d.ItemID = i.ItemID
       AND ( d.percentOff > 0 OR d.dollarOff > 0 )

我在SELECT列表中省略了ItemStuff.StuffID,因为我没有看到任何ItemStuff行源。

我也排除了WHERE子句,因为我在查询中没有看到任何ItemStuff行源。

-- WHERE ItemStuff.ItemID = i.ItemID

我省略了GROUP BY,因为我再次没有在你的查询中看到任何ItemStuff行源,并且因为GROUP BY的行为可能不是我所期望的,而是掩盖了我的查询中的问题。 / p>

-- GROUP BY ItemStuff.StuffID

更新:

@Kyle,您的查询“超时”这一事实让我相信您正在生成比预期更多的行,就像您有一个笛卡尔积(表中的每一行都与某些行“匹配”)其他表...一个表中有10,000行,另一个表中有10,000行,这将产生100,000,000行。

我认为GROUP BY条款掩盖了真正的问题。

我建议您在开发时将每个表的PRIMARY KEY包含在结果集中的前导列中。我会在驱动表中添加一些合理的谓词(例如i.ItemID IN (2,3,5,7)来限制结果集的大小,以及ORDER BY主键...这应该可以帮助您识别非预期的笛卡尔积。

答案 1 :(得分:0)

从查询中删除这些行后,您是否得到了所需的内容?

AND(     ItemDiscounts.percentOff> 0     OR ItemDiscounts.dollarOff> 0   )

答案 2 :(得分:0)

为外连接的可能为空的一侧指定绝对值后,您的WHERE子句必须考虑它。

使用以下子句尝试:

AND (
ItemDiscounts.percentOff > 0
OR ItemDiscounts.percentOff is null
OR ItemDiscounts.dollarOff > 0
OR ItemDiscounts.dollarOff is null
)

另外,您指定的是没有聚合的GROUP BY。在大多数情况下,这没有任何意义。您可能希望ORDER BY进行排序。