MySQL INNER / LEFT JOIN在3个表上,其中第3个表中的记录可能不存在

时间:2013-08-24 04:57:18

标签: mysql join left-join inner-join

我遇到了一些问题,在一系列尝试失败之后我似乎无法弄明白。

我有三个表需要为某些报告进行连接,而在第3个表中,可能不存在记录。但是如果第3个表中的记录不存在,我需要为来自第3个表的数据报告空值并获取与其他条件匹配的所有记录。

剥离到相关列,这里是表结构:

成员 - 此表包含注册到网站的所有成员

| memberId  | insertDate          |
| ==========|=====================|
| 1         | 2013-08-01 18:18:16 |
| 2         | 2013-08-02 18:18:16 |
| 3         | 2013-08-03 18:18:16 |
| 4         | 2013-08-04 18:18:16 |
| 5         | 2013-08-05 18:18:16 |

registration_steps - 此表包含注册流程的进度以及注册是否已完成

| memberId  | completed |
| ==========|===========|
| 1         | 1         |
| 2         | 1         |
| 3         | 1         |
| 4         | 0         |
| 5         | 1         |

购买 - 此表保留,以及...购买

| memberId  | insertDate          |
| ==========|=====================|
| 1         | 2013-08-02 18:18:16 |
| 1         | 2013-08-03 17:18:16 |
| 1         | 2013-08-03 18:18:16 |
| 5         | 2013-08-07 18:18:16 |

这是我到目前为止提出的问题:

SELECT `m`.`memberId`,
       DATE(`m`.`insertDate`) AS `regDate`,
       COUNT(`p`.`memberId`) AS `totalTransactions`,
       DATE(MIN(`p`.`insertDate`)) AS `firstPurchaseDate`,
       DATE(MAX(`p`.`insertDate`)) AS `latestPurchaseDate`,
       DATEDIFF(DATE(MIN(`p`.`insertDate`)), DATE(`m`.`insertDate`)) AS `daysBetweenRegAndFirstPurchase`
  FROM `db`.`members` `m`
       INNER JOIN `db`.`registration_steps` `r` ON `m`.`memberId` = `r`.`memberId` 
       INNER JOIN `db`.`purchases` `p` ON `m`.`memberId` = `p`.`memberId`
 WHERE `m`.`insertDate` BETWEEN '2013-07-01 00:00:00' AND '2013-07-31 23:59:59'
   AND `r`.`completed` = 1
GROUP BY `m`.`memberId`
;

它向我显示了我想要的所有内容,但是在桌面购买中缺少记录的成员。

这是我得到的:

| memberId  | regDate             | totalTransactions | firstPurchaseDate   | latestPurchaseDate  | daysBetweenRegAndFirstPurchase |
| ==========|=====================|===================|=====================|=====================|================================|
| 1         | 2013-08-01 18:18:16 | 3                 | 2013-08-02 18:18:16 | 2013-08-03 18:18:16 | 1                              |
| 5         | 2013-08-05 18:18:16 | 1                 | 2013-08-07 18:18:16 | 2013-08-07 18:18:16 | 2                              |

但我需要的是:

| memberId  | regDate             | totalTransactions | firstPurchaseDate   | latestPurchaseDate  | daysBetweenRegAndFirstPurchase |
| ==========|=====================|===================|=====================|=====================|================================|
| 1         | 2013-08-01 18:18:16 | 3                 | 2013-08-02 18:18:16 | 2013-08-03 18:18:16 | 1                              |
| 2         | 2013-08-02 18:18:16 | 0                 | NULL                | NULL                | -1                             |
| 3         | 2013-08-03 18:18:16 | 0                 | NULL                | NULL                | -1                             |
| 5         | 2013-08-05 18:18:16 | 1                 | 2013-08-07 18:18:16 | 2013-08-07 18:18:16 | 2                              |

为了实现这一点,我尝试将第二个内连接更改为左连接,左外连接并将where条件放入第一个内连接条件。但是,我无法获得理想的结果。 (必须承认我已经中断了一些非常长时间运行的查询,这些查询可能是正确的(?)但是(真实场景中成员的总数大约是20k)。)

任何?

提前致谢!

3 个答案:

答案 0 :(得分:2)

要从member表中获取所有结果,您需要left join其他表格,并为group by表格添加member

<强>示例

FROM `db`.`members` `m`
       LEFT OUTER JOIN `db`.`registration_steps` `r` ON `m`.`memberId` = `r`.`memberId` 
       LEFT OUTER JOIN `db`.`purchases` `p` ON `m`.`memberId` = `p`.`memberId`

LEFT [OUTER]

指定左表中不满足连接条件的所有行都包含在结果集中,并且除了内连接返回的所有行之外,另一个表中的输出列都设置为NULL。

答案 1 :(得分:1)

(假设查询中的日期范围应介于2013-08而非2013-07之间)。您需要将联接与购买切换为外部联接。这将包括没有任何购买的成员......

   left outer JOIN `purchases` `p` ON `m`.`memberId` = `p`.`memberId`

SQL Fiddle

答案 2 :(得分:0)

(用左连接替换内连接,你会得到正确的结果)

FROM dbmembers m        左加入dbregistration_steps r点击mmemberId = rmemberId        离开加入dbpurchases p点击mmemberId = pmemberId

或者您可以使用左连接替换任何购买。

注意:左连接和左外连接都会产生相同的结果,因此您可以使用其中任何一个。