MySQL Union Operator的行为不符合预期

时间:2014-04-15 22:35:12

标签: mysql union

我通常在这里找到问题的答案。如果没有,我知道这可能是因为我看起来不够深。但是这一次,我所看到的答案都不符合我的需求。所以这就是:

我想使用MySQL服务器从两个表构建报告:

ID              Email                  ID     Date      Spent
-----------------------              ---------------------------
A123         a@test.com               A123      3.3.14     2.50
B102         b@test.com               A123      7.3.14     3.50
yum          yum@a.com                B102      4.4.14     7.00
(null)         (null)

我想针对给定的时间戳进行报告,例如。从3.1.2014到3.31.14,我得到了系统的所有ID的列表,以及他们发送的相应金额,即使他们没有花费任何东西。

因此,从上表中,我们想要检索2014年3月的月份(从3.1.2014到3.31.14),我想得到:

ID          Spent
------------------
A123         6.00
B102         7.00
yum          NULL

我不介意为那些没有花钱的用户提供NULL或0。到目前为止,我已经得到了这个:

SELECT ID,
   sum(Spent) AS Spent
FROM expenses
WHERE expenses.date >= '2014-03-01 00:00:00'
  AND expenses.date < '2014-03-31 00:00:00'
GROUP BY expenses.ID
UNION
SELECT users.ID,
   NULL AS Spent
FROM users
WHERE users.ID NOT IN
 (SELECT expenses.ID
   FROM expenses
   WHERE expenses.date >= '2014-03-01 00:00:00'
   AND expenses.date < '2014-03-31 00:00:00'
 GROUP BY expenses.ID)
ORDER BY ID;

您可以在here中查看以上代码。

它按预期工作,但在我的数据库的实际情况中,结果查询中的行数大于users中唯一行的数量。

我已经检查过expenses表中的所有ID都在users表中,情况就是这样。我测试过:

SELECT ID FROM expenses
  WHERE expenses.date >= '2014-03-01 00:00:00'
 AND expenses.date < '2014-03-31 00:00:00'
 AND ID NOT IN (SELECT users.ID FROM users);

并返回一个空集,这与预期的一样。

我必须遗漏一些东西,但我不知道是什么。有人可以提供一些见解吗?我对MySQL很陌生,也许还有更好的方法。

3 个答案:

答案 0 :(得分:2)

我认为您需要left outer join而不是union

SELECT u.ID, sum(e.Spent) AS Spent
FROM users u left outer join
     expenses e
     on u.id = e.id and
        e.date >= '2014-03-01 00:00:00' and
        e.date < '2014-03-31 00:00:00'
GROUP BY u.ID;

请注意,在SQL Fiddle中的数据中,这将返回四行,因为NULL中的users值行。

答案 1 :(得分:0)

您可以连接两个表(用户和费用)以获得相同的结果(http://sqlfiddle.com/#!2/738ea6/5)。这将更加快速且易于理解。

SELECT expenses.ID,
   sum(Spent) AS Spent
FROM expenses, users 
WHERE expenses.date >= '2014-03-01 00:00:00'
  AND expenses.date < '2014-03-31 00:00:00'
  AND users.ID = expenses.ID
GROUP BY expenses.ID
ORDER BY ID;

答案 2 :(得分:0)

在第二个查询中分组?

    SELECT ID,
   sum(Spent) AS Spent
FROM expenses
WHERE expenses.date >= '2014-03-01 00:00:00'
  AND expenses.date < '2014-03-31 00:00:00'
GROUP BY expenses.ID
UNION
SELECT users.ID,
   NULL AS Spent
FROM users
WHERE users.ID NOT IN
 (SELECT expenses.ID
   FROM expenses
   WHERE expenses.date >= '2014-03-01 00:00:00'
   AND expenses.date < '2014-03-31 00:00:00'
 GROUP BY expenses.ID)
GROUP BY ID
ORDER BY ID;