MySQL外连接替换

时间:2015-04-28 01:02:49

标签: mysql full-outer-join

我正在开发一个游戏库存管理系统,并希望在一张表中显示所有者的进货希望清单和每个游戏的客户购买预订计数。我写了一个我认为有效的查询,但后来我注意到它实际上省略了任何有预订的游戏,但最初都没有在补货愿望清单中。查询如下:

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, 数量, gameName, payYes, payNo

1, 4, 适合所有季节的城堡, 0, 0

2, 2, 几英亩的雪, 0, 0

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

54, 2, Big Boggle, 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,quantity,gameID,payYes,payNo

1,4,NULL,NULL,NULL

2,2,NULL,NULL,NULL

18,4,NULL,NULL,NULL

54,2,52,2,0

NULL,NULL,30,3,1

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

我希望查询显示为我最初编写的,只是使用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);

示例数据: RestockWishList:

游戏ID, 量

1, 4

2, 2

18, 4

54, 2

ReservationsBuy:

游戏ID, customerEmail, 顾客姓名, dateReserved, 购买日期, dateClaimed, 预付费

30, wonder@woman.com, 戴安娜, 2015-04-24 14:46:05, 空值, 空值, 是

54, boggie@marsh.com, 博吉, 2015-04-24 14:43:32, 空值, 空值, 是

54, manny@second.com, 曼尼, 2015-04-27 19:48:22, 空值, 空值, 是

43, old@mom.com, 奶奶, 2015-04-23 22:32:03, 空值, 空值, 否

预期产量: gameID,quantity,gameName,payYes,payNo

1,4,所有季节的城堡,0,0

2,2,几英亩雪,0,0

18,4,Alhambra,0,0

30,0,Arkham Horror,1,0

43,0,Bananagrams,0,1

54,2,Big Boggle,2,0

(游戏表对此查询不是特别重要。只有相关性是ReservationsBuy和RestockWishList都通过gameID连接到游戏)

3 个答案:

答案 0 :(得分:1)

我想也许您想要这样的查询 - 不是完整的外部联接:

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

demo here,但它的要点是,对于游戏桌中的每个游戏,它会给你预订总数,愿望清单中的数量,我们使用条件计数以提供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; prepaid = yes的计数。实际上它只是在共享的gameid上加入两个小查询。

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

答案 1 :(得分:0)

您使用FULL OUTER JOIN进入了正确的轨道,您只是执行不正确。

MySQL中的FULL OUTER JOIN可以被认为是UNIONLEFT JOIN的{​​{1}}。在您的查询中,您试图通过将逻辑的RIGHT JOIN部分视为两个表格的反RIGHT JOIN来尝试近似,但您的第一部分不起作用,因为它&& #39;不是与您的第一个LEFT JOIN具有相同GROUP BY序列的子选择。

最简单的方法是将第一个LEFT JOIN查询节简单地复制到第二节,然后将LEFT JOIN替换为LEFT JOIN,然后将结果链接到您的游戏表,像这样:

RIGHT JOIN

SQL Fiddle Results

答案 2 :(得分:0)

确定。所以我们现在总是在game表中存在一条记录,对吗?

然后使用此表启动您的FROM,然后您只需为每个表执行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
;

正如您所说,唯一的变化是:使用FROM表开始Games并使用LEFT JOIN,同时从WHERE中移除条件并将其放入LEFT JOIN