在 1 table 上合并多个 MySQL 查询(带分组依据)的结果

Combine results of multiple MySQL queries (w/ Group By) on 1 table

最好的方法是什么?

我将在下面进行描述,但这里是 rextester.com 数据的设置 - http://rextester.com/PXQOV60475

我有一个名为 "cases" 的 table,其中基本上包含有关维修工作的信息。 Case Manager 有一个名为 "case_mgr" 的字段,还有一个 "case_mgr_first" 字段,因为 Case Manager 可以更改,因此它包含原始字段。 "who_last_called" 还有另一个类似的字段,它是最后联系客户的用户。这些都包含用户名...尽管 "case_mgr_first" 和 "who_last_called" 可能为空("case_mgr_first" 是一个新字段,可能没有人调用过)。

要继续修复工作,必须接收要修复的项目。收到后,字段 "item_received_date" 被设置,否则为空。 "created_date".

字段中还保存了记录的创建日期

因此,目标是通过多种方式为用户找到接收百分比。我想找到当前案例经理 ("case_mgr")、"case_mgr_first" 和 "who_last_called"... 的用户在特定时间段内的接收率 "created_date".

我已经对其中之一进行了查询,而且效果很好。

SELECT c.case_mgr AS case_mgr, COUNT(*) AS count_new, 
SUM(CASE WHEN c.item_received_date IS NOT NULL THEN 1 ELSE 0 END) AS count_recd, 
SUM(CASE WHEN c.item_received_date IS NOT NULL THEN 1 ELSE 0 END)*100/COUNT(*) AS percent_recd
FROM cases c WHERE (c.created_date >= '2017-05-01 00:00:00' AND c.created_date <= '2017-05-31 23:59:59') 
GROUP BY c.case_mgr ORDER BY c.case_mgr ASC

这给了我一个结果-

+-----------+-----------+------------+--------------+
| case_mgr  | count_new | count_recd | percent_recd |
+-----------+-----------+------------+--------------+
| bamm-bamm |        10 |          4 | 40.00        |
| barney    |       105 |         43 | 40.95        |
| betty     |       120 |         60 | 50.00        |
| fred      |       139 |         54 | 38.85        |
| wilma     |        97 |         56 | 57.73        |
+-----------+-----------+------------+--------------+

经过"case_mgr_first"时我也这样做。

SELECT c.case_mgr_first AS case_mgr_first, COUNT(*) AS count_new, 
SUM(CASE WHEN c.item_received_date IS NOT NULL THEN 1 ELSE 0 END) AS count_recd, 
SUM(CASE WHEN c.item_received_date IS NOT NULL THEN 1 ELSE 0 END)*100/COUNT(*) AS percent_recd 
FROM cases c WHERE (c.created_date >= '2017-05-01 00:00:00' AND c.created_date <= '2017-05-31 23:59:59') 
GROUP BY c.case_mgr_first ORDER BY c.case_mgr_first ASC

这给了我一个结果-

+----------------+-----------+------------+--------------+
| case_mgr_first | count_new | count_recd | percent_recd |
+----------------+-----------+------------+--------------+
| NULL           |       137 |         62 | 45.26        |
| barney         |        84 |         44 | 52.38        |
| betty          |        72 |         37 | 51.39        |
| fred           |       116 |         47 | 40.52        |
| wilma          |        61 |         19 | 31.15        |
+----------------+-----------+------------+--------------+

(注意bamm-bamm出现在第一个结果中,第二个结果中没有,第二个结果中有一个NULL条目。)

我想要一个看起来像这样的组合结果(我删除了 count_new 和 count_recd 以便于阅读)-

+-----------+-----------------------+-----------------------------+
|  user     | percent_recd_case_mgr | percent_recd_case_mgr_first |
+-----------+-----------------------+-----------------------------+
| NULL      | NULL                  | 45.26                       |
| bamm-bamm | 40.00                 | NULL                        |
| barney    | 40.95                 | 52.38                       |
| betty     | 50.00                 | 51.39                       |
| fred      | 38.85                 | 40.52                       |
| wilma     | 57.73                 | 31.15                       |
+-----------+-----------------------+-----------------------------+

通过使用子查询并加入这些子查询,我已经非常接近了,并且用户组合正确,但问题是使用 LEFT JOIN 我丢失了第二个查询的结果,该结果没有出现在首先是用户为 NULL 的地方,如果是 RIGHT JOIN,我会丢失第一个不在第二个中的结果。另外,它的查询持续时间似乎只是子查询的总和,这可能无法改进,我不确定。

这是我试过的查询-

SELECT * FROM 
(
SELECT c.case_mgr AS case_mgr, SUM(CASE WHEN c.item_received_date IS NOT NULL THEN 1 ELSE 0 END)*100/COUNT(*) AS percent_recd
FROM cases c WHERE (c.created_date >= '2017-05-01 00:00:00' AND c.created_date <= '2017-05-31 23:59:59') 
GROUP BY c.case_mgr
) a
LEFT JOIN
(
SELECT c.case_mgr_first AS case_mgr_first, SUM(CASE WHEN c.item_received_date IS NOT NULL THEN 1 ELSE 0 END)*100/COUNT(*) AS percent_recd
FROM cases c WHERE (c.created_date >= '2017-05-01 00:00:00' AND c.created_date <= '2017-05-31 23:59:59') 
GROUP BY c.case_mgr_first 
) b
ON a.case_mgr = b.case_mgr_first 
ORDER BY a.case_mgr ASC

这是结果-

+-----------+-----------------------+----------------+-----------------------------+
| case_mgr  | percent_recd_case_mgr | case_mgr_first | percent_recd_case_mgr_first |
+-----------+-----------------------+----------------+-----------------------------+
| bamm-bamm | 50.00                 | NULL           | NULL                        |
| barney    | 40.95                 | barney         | 52.38                       |
| betty     | 50.00                 | betty          | 51.39                       |
| fred      | 38.85                 | fred           | 40.52                       |
| wilma     | 57.73                 | wilma          | 31.15                       |
+-----------+-----------------------+----------------+-----------------------------+

我可以使用 2 个查询来执行此操作并将它们合并到代码中,但是将它们包含在一个查询中会很好,尤其是在可以以某种方式提高性能的情况下。

通过更多阅读,我明白这就像其他 SQL 中的 FULL OUTER JOIN 而在 MySQL 中不存在。它在 MySQL 中通过 LEFT JOIN 和 RIGHT JOIN 的 UNION 模拟。好的,现在我试过了,它确实有效,但是哇,它需要 0.92 秒(如果添加另一个字段,比如我在开头提到的 "who_last_called",那将是非常糟糕的)。最初的 2 个查询每个都花费了大约 0.22 秒,而我第一次尝试 JOIN 花费了 0.50 秒。 "case_mgr" 字段有索引,但 "case_mgr_first".

没有

如有任何帮助或建议,我们将不胜感激!有没有更好的方法,或者我应该坚持使用单个查询并将它们放在代码中?

我猜你可以想出一个解决方案,你可以做一个 UNION sub select 并在那个 sub select 上再次分组,但它不会很漂亮:

SELECT tmp.mgr,
  SUM(tmp.percent_recd_case_mgr) AS percent_recd_case_mgr,
  SUM(tmp.percent_recd_case_mgr_first) AS percent_recd_case_mgr
FROM ((
  -- this the first part will basically contain the case_mgr data
  SELECT
    c.case_mgr AS mgr,
    SUM(
      CASE WHEN c.item_received_date IS NOT NULL THEN 1 ELSE 0 END
    )*100/COUNT(*) AS percent_recd_case_mgr,
    0 AS percent_recd_case_mgr_first -- 0 as third column
  FROM cases c
  WHERE
    c.created_date >= '2017-05-01 00:00:00'
    AND c.created_date <= '2017-05-31 23:59:59'
  GROUP BY c.case_mgr ORDER BY c.case_mgr ASC
) UNION (
  -- And the second part contains the case_mgr_first data
  SELECT
    c.case_mgr_first AS mgr,
    0 AS percent_recd_case_mgr, -- 0 as second column
    SUM(
      CASE WHEN c.item_received_date IS NOT NULL THEN 1 ELSE 0 END
    )*100/COUNT(*) AS percent_recd_case_mgr_first
  FROM cases c
  WHERE
    c.created_date >= '2017-05-01 00:00:00'
    AND c.created_date <= '2017-05-31 23:59:59'
  GROUP BY c.case_mgr_first ORDER BY c.case_mgr_first ASC
)) AS tmp -- together both parts form a temp table and we sum again
          -- over all records
GROUP BY tmp.mgr
ORDER BY tmp.mgr ASC;