MySQL 外连接替换

MySQL outer join substitute

我正在开发一个游戏库存管理系统,我想在单个 table 中显示所有者的补货愿望清单和每个游戏的客户购买预订数量。我写了一个我认为有用的查询,但后来我注意到它实际上忽略了任何有保留但最初不在补货愿望清单中的游戏。查询如下:

SELECT rwl.*, g.gameName, coalesce(payYes, 0) payYes, coalesce(payNo, 0) payNo FROM RestockWishList AS rwl, Games AS g
        LEFT JOIN
        (SELECT gameID, COUNT(if(prepaid='Yes', 1, NULL)) payYes, COUNT(if(prepaid='No', 1, NULL)) payNo FROM ReservationsBuy GROUP BY gameID) AS res
        ON res.gameID = g.gameID
        WHERE rwl.gameID = g.gameID;

查询结果: 游戏ID, 数量, 游戏名称, 支付是的, 付款编号

1, 4、 四季皆宜的城堡, 0, 0

2, 2、 几亩雪, 0, 0

18, 4、 阿尔罕布拉宫, 0, 0

54, 2、 大傻瓜, 2、 0

显然这个问题的解决方案是使用 FULL OUTER JOIN 而不是 LEFT JOIN,但是 MySQL 不支持该功能。我花了几个小时试图将其转换为 UNION 结构,但无法使其正常工作。这和我得到的一样接近:

SELECT rwl.*, res.gameID, res.payYes, res.payNo FROM RestockWishList rwl
LEFT JOIN
(SELECT gameID, COUNT(if(prepaid='Yes', 1, NULL)) payYes, COUNT(if(prepaid='No', 1, NULL)) payNo FROM ReservationsBuy GROUP BY gameID) AS res
        ON res.gameID = rwl.gameID
UNION
SELECT rwl.*, res.gameID, COUNT(if(prepaid='Yes', 1, NULL)) payYes, COUNT(if(prepaid='No', 1, NULL)) payNo FROM ReservationsBuy res
LEFT JOIN RestockWishList rwl ON rwl.gameID = res.gameID;

查询结果: gameID, 数量, gameID, payYes, payNo

1、4、空、空、空

2、2、空、空、空

18, 4, 空值, 空值, 空值

54、2、54、2、0

空值、空值、30、3、1

(抱歉,我不知道如何在 Whosebug 中很好地格式化查询 table 结果。)

我希望查询按我最初编写的那样显示,只是缺少 ReservationsBuy 中的值。请具体帮助?

表:

CREATE TABLE IF NOT EXISTS RestockWishList (
gameID INT(6),
quantity INT(3) NOT NULL,
PRIMARY KEY (gameID),
FOREIGN KEY (gameID) REFERENCES Games(gameID) ON UPDATE CASCADE ON DELETE CASCADE);

CREATE TABLE IF NOT EXISTS ReservationsBuy (
gameID INT(6),
customerEmail VARCHAR(25) NOT NULL,
customerName VARCHAR(25) NOT NULL,
dateReserved DATETIME NOT NULL,     #date customer files game reservation
datePurchased DATETIME,             #date Board and Brew restocks game
dateClaimed DATETIME,               #date customer physically claims game
prepaid ENUM('Yes', 'No') NOT NULL,
PRIMARY KEY (gameID, customerEmail),
FOREIGN KEY (gameID) REFERENCES Games (gameID) ON UPDATE CASCADE ON DELETE CASCADE);

示例数据: 补货愿望清单:

游戏ID, 数量

1, 4

2, 2

18, 4

54, 2

预订购买:

游戏ID, 客户邮箱, 顾客姓名, 日期保留, 购买日期, 申请日期, 预付

30, wonder@woman.com, 戴安娜, 2015-04-24 14:46:05, 无效的, 无效的, 是

54, boggie@marsh.com, 转向架, 2015-04-24 14:43:32, 无效的, 无效的, 是

54, 曼尼@second.com, 曼尼, 2015-04-27 19:48:22, 无效的, 无效的, 是

43, 老@mom.com, 奶奶, 2015-04-23 22:32:03, 无效的, 无效的, 否

预期输出: gameID, quantity, gameName, payYes, payNo

1, 4, 四季城堡, 0, 0

2, 2, 几亩雪, 0, 0

18, 4, 阿罕布拉, 0, 0

30, 0, 阿卡姆恐怖, 1, 0

43, 0, 香蕉字谜, 0, 1

54, 2, Big Boggle, 2, 0

(游戏 table 对于此查询不是特别重要。唯一相关的是 ReservationsBuy 和 RestockWishList 都通过游戏 ID 连接到游戏)

您使用 FULL OUTER JOIN 的方法是正确的,只是实施不正确。

MySQL中的FULL OUTER JOIN可以认为是LEFT JOINRIGHT JOINUNION。在您的查询中,您试图通过将逻辑的 RIGHT JOIN 部分视为两个 table 的逆 LEFT JOIN 来近似此,但您的第一部分不起作用,因为它不是与您的第一个 LEFT JOIN.

具有相同 GROUP BY 序列的子选择

最简单的做法是将第一个 LEFT JOIN 查询节复制到第二个节,然后将 LEFT JOIN 替换为 RIGHT JOIN,然后 link结果到你的游戏 table,像这样:

SELECT g.gameID, IFNULL(q.quantity, 0) AS quantity, g.gameName,
       IFNULL(q.payYes, 0) AS payYes, IFNULL(q.payNo, 0) AS payNo
  FROM games g
       INNER JOIN (
         SELECT IFNULL(rwl.gameID, res.gameID) AS gameID, rwl.quantity,
                res.payYes, res.payNo
           FROM RestockWishList rwl
                LEFT JOIN (
                   SELECT gameID, COUNT(if(prepaid='Yes', 1, NULL)) payYes,
                          COUNT(if(prepaid='No', 1, NULL)) payNo
                     FROM ReservationsBuy
                 GROUP BY gameID
                ) AS res ON res.gameID = rwl.gameID
          UNION
         SELECT IFNULL(rwl.gameID, res.gameID) AS gameID, rwl.quantity,
                res.payYes, res.payNo
           FROM RestockWishList rwl
                RIGHT JOIN (
                    SELECT gameID, COUNT(IF(prepaid='Yes', 1, NULL)) payYes,
                           COUNT(IF(prepaid='No', 1, NULL)) payNo
                      FROM ReservationsBuy
                  GROUP BY gameId
                ) AS res ON res.gameID = rwl.gameID
       ) AS q ON g.gameID = q.gameID

SQL Fiddle Results

我想您可能想要这样的查询 - 而不是完整的外部联接:

select q.id, q.name, q.reservations, ifnull(q2.wishcount, 0) wishcount, q.payYes, q.payNo
  from (
    select g.*, count(rb.gameid) reservations, count(case when prepaid = 'Yes' then 1 end) payYes, count(case when prepaid = 'No' then 1 end) payNo
      from games g
        left join reservationsbuy rb
          on g.id = rb.gameid
      group by g.id
  ) q
  left join (
    select g.id, sum(quantity) wishcount
      from games g
        left join restockwishlist rwl
          on g.id = rwl.gameid
      group by g.id
  ) q2
  on q.id = q2.id;

有一个demo here,但它的要点是,对于游戏中的每个游戏table,它会为您提供预订总数,愿望清单中的数量,以及我们使用条件计数来提供 prepaid = yesprepaid = no 的计数。实际上,它只是将共享游戏 ID 上的两个小查询连接在一起。

如果您希望这包括按日期等过滤,您可能需要更明确地说明您希望结果如何工作,或如何显示

好的。所以我们现在总是在 game table 中存在一条记录,对吗?

然后用这个 table 开始你的 FROM,然后你只需要为每个 table 做一个 LEFT JOIN,如下所示:

SELECT
   rwl.*
 , g.gameName
 , coalesce(payYes, 0) payYes
 , coalesce(payNo, 0) payNo
FROM
  Games AS g LEFT JOIN
    RestockWishList AS rwl ON rwl.gameID = g.gameID LEFT JOIN
      (
         SELECT
            gameID
          , COUNT(if(prepaid='Yes', 1, NULL)) payYes
          , COUNT(if(prepaid='No', 1, NULL)) payNo
         FROM
           ReservationsBuy
         GROUP BY gameID) AS res ON res.gameID = g.gameID
;

正如您所说,唯一的变化是:用 Games table 启动 FROM 并使用 LEFT JOIN,同时从 [=17= 中删除条件] 并将其放入 LEFT JOIN