MYSQL JOIN给出了意想不到的(但令人愉快的)结果?

时间:2012-02-17 18:53:42

标签: mysql join left-join

嘿伙计们,我正在摸着这个,我希望有人可以解释一下 我。老实说,我有点害怕以后可能会把我咬到屁股......

所以,我现在有三张桌子。 reportsberriesmelons。我设置了这样的查询,它让我得到了我想要的东西。

SELECT 
   rpt.*, 
   ber.shipper, ber.po, ber.commodity, ber.label

FROM reports rpt

LEFT JOIN berries ber ON rpt.inspection_number = ber.report_key
LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key

WHERE rpt.status='1'  OR rpt.status='0'
ORDER BY rpt.inspection_number DESC

我得到了预期的回报

key | role | region   | inspection_type | inspection_number | shipper   | po    | commodity     | label
3   | NULL | Seattle  | melons          | 5555              | Shipper1  | PO2   | Commodity2    | Label2
2   | NULL | Seattle  | berries         | 1023              | Shipper1  | PO1   | Commodity1    | Label1

如果我从我的陈述中删除LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key,我会得到完全相同的东西......我从未提及melons ??

如果我修改并使用JOIN代替浆果

SELECT 
   rpt.*, 
   ber.shipper, ber.po, ber.commodity, ber.label

FROM reports rpt

JOIN berries ber ON rpt.inspection_number = ber.report_key

WHERE rpt.status='1'  OR rpt.status='0'
ORDER BY rpt.inspection_number DESC

它产生我预期的应该!

key | role | region   | inspection_type | inspection_number | shipper   | po    | commodity     | label
2   | NULL | Seattle  | berries         | 1023              | Shipper1  | PO1   | Commodity1    | Label1

但是试图修改我的SQL语句......

SELECT 
   rpt.*, 
   ber.shipper, ber.po, ber.commodity, ber.label
   mel.shipper, mel.po, mel.commodity, mel.label

FROM reports rpt

JOIN berries ber ON rpt.inspection_number = ber.report_key
JOIN melons mel ON rpt.inspection_number = mel.report_key

WHERE rpt.status='1'  OR rpt.status='0'
ORDER BY rpt.inspection_number DESC

Nets me ....

MySQL returned an empty result set (i.e. zero rows). ( Query took 0.0011 sec ) 给了我大中指。我勒个去?有人可以解释我显然做错了什么,以及如何解决它?

3 个答案:

答案 0 :(得分:5)

这不是那么复杂。你的第一个问题,你是加入反对梅尔但从未做过任何事情,所以你只得到贝尔的数据。你的最后一个查询更接近,但因为你内心加入浆果和甜瓜而且你没有任何两个的报告,你就得不到任何结果。但答案更接近你在第二个查询中所做的事情,我认为你想要的是:

SELECT 
   rpt.*, 
   COALESCE(ber.shipper, mel.shipper) AS shipper,
   COALESCE(ber.po, mel.po) AS po, 
   COALESCE(ber.commodity, mel.commodity) AS commodity,
   COALESCE(ber.label, mel.label) AS label
FROM reports rpt
LEFT JOIN berries ber ON rpt.inspection_number = ber.report_key
LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key
WHERE rpt.status='1'  OR rpt.status='0'
ORDER BY rpt.inspection_number DESC

这个查询说,给我一些有浆果或甜瓜连接的行,但是对于它们共有的列,给我任何一个存在的。我们没有特别的原因,我们先服用ber。

假设这两个表是互斥的,我认为这可以做你想要的。

编辑:根据@MarcusAdams在下面指出的内容,如果有一些令人讨厌的水果表,可以将其重写为使用UNION

SELECT report_key, shipper, po, commodity, label FROM berries
UNION
SELECT report_key, shipper, po, commodity, label FROM melons
UNION
SELECT report_key, shipper, po, commodity, label FROM ...
...

此查询将为您提供一些方便的功能,您可以在以后将其用作子查询(或视图)。您也可以硬编码原始名称,如下所示:

SELECT report_key, shipper, po, commodity, label, 'berries' AS type FROM berries
UNION
SELECT report_key, shipper, po, commodity, label, 'melons' FROM melons
UNION
SELECT report_key, shipper, po, commodity, label, '...' FROM ...
...

然后在原始查询中使用它,你可以这样嵌入它:

SELECT *
FROM reports rpt,
JOIN (SELECT report_key, shipper, po, commodity, label, 'berries' AS type FROM berries
      UNION
      SELECT report_key, shipper, po, commodity, label, 'melons' FROM melons
      UNION
      SELECT report_key, shipper, po, commodity, label, '...' FROM ...
      ...) fruits ON rpt.inspection_number = fruits.report_key
WHERE rpt.status='1'  OR rpt.status='0'
ORDER BY rpt.inspection_number DESC

答案 1 :(得分:3)

Daniel Lyons提供的以下查询效果很好,但我想稍微讨论一下,并且严格用于学术目的,提供另一种解决方案,可能会更加优化。

这是丹尼尔的询问:

SELECT 
   rpt.*, 
   COALESCE(ber.shipper, mel.shipper) AS shipper,
   COALESCE(ber.po, mel.po) AS po, 
   COALESCE(ber.commodity, mel.commodity) AS commodity,
   COALESCE(ber.label, mel.label) AS label
FROM reports rpt
LEFT JOIN berries ber ON rpt.inspection_number = ber.report_key
LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key
WHERE rpt.status='1'  OR rpt.status='0'
ORDER BY rpt.inspection_number DESC

这个查询效果很好,只有两个水果,它相当优化。仍然因为报告是互斥的,所以查询正在尝试额外的连接而不是必要的。例如,如果报告记录已经加入浆果记录,我们知道它不会加入甜瓜记录,但MySQL不知道。相反,MySQL将进行另一次查找以尝试连接到瓜表,即使找不到相应的记录。

只有两个连接,一半的连接尝试都被浪费了。然而,有三个果实,三分之二的连接尝试被浪费,有四个果实,四分之三的连接尝试被浪费,等等。

为了避免额外的连接尝试,我们可以颠倒连接的顺序,如下所示:

(SELECT rpt.*, ber.shipper, ber.po, ber.commondity, ber.label
FROM berries ber
JOIN reports rpt
  ON rpt.inspection_number = ber.report_key
WHERE rpt.status = '1' OR rpt.status = '0')
UNION ALL
(SELECT rpt.*, mel.shipper, mel.po, mel.commondity, mel.label
FROM melons mel
JOIN reports rpt
  ON rpt.inspection_number = mel.report_key
WHERE rpt.status = '1' OR rpt.status = '0')
ORDER BY inspection_number DESC

在这里,我们从另一个方向(带有水果)开始,然后加入到报告中。这允许我们为每个报告只创建一个连接。

请注意,我们现在每个水果都使用INNER JOIN而不是LEFT JOIN,我们正在使用UNION ALL将每个水果的结果加入到一个更大的结果集中。< / p>

为了进一步优化,有时MySQL不会将10这两个常量识别为范围,特别是如果它不是整数字段。范围查找比两个单独的查找更快,因此要向MySQL提示10的rpt.status是范围,请使用BETWEEN而不是OR,假设您的覆盖索引为(rpt.inspection_number, rpt.status)

答案 2 :(得分:0)

首先,你会注意到这个SELECT查询没有从瓜表中选择任何数据。因此,您不需要加入瓜表,因为您没有从中选择任何数据,并且它并不重要,因为您没有从中选择任何内容,因此无论它是什么或引用它。

SELECT 
   rpt.*, 
   ber.shipper, ber.po, ber.commodity, ber.label

接下来,我不太熟悉LEFT JOININNER JOINJOIN之间的差异,所以我无法回答这个问题。但是,根据我对MySQL的经验,尝试将最后一个查询更改为INNER JOIN而不是JOIN,看看是否有效。 MySQL网站上有documentation关于不同类型连接之间的差异。

如果您仍然遇到问题,请告诉我,我会尽力帮助您。希望我至少回答了一些问题。