【问题标题】:Combine results of multiple MySQL queries (w/ Group By) on 1 table在 1 个表上组合多个 MySQL 查询(w/Group By)的结果
【发布时间】:2017-06-15 22:08:18
【问题描述】:

最好的方法是什么?

我将在下面进行描述,但这里有一个在 rextester.com 上的数据可供使用 - http://rextester.com/PXQOV60475

我有一个名为“案例”的表格,其中基本上包含有关维修工作的信息。 Case Manager 有一个名为“case_mgr”的字段,还有一个名为“case_mgr_first”的字段,因为 Case Manager 可以更改,因此它包含原始文件。 “who_last_call”还有另一个类似的字段,它是最后一次联系客户的用户。这些都包含用户名......虽然“case_mgr_first”和“who_last_call”可能为空(“case_mgr_first”是一个新字段,可能没有人调用)。

要继续维修工作,必须收到要维修的物品。收到后,设置字段“item_received_date”,否则为空。记录的创建日期也保存在“created_date”字段中。

因此,我们的目标是通过多种方式为用户找到接收百分比。我想在“created_date”之前的某个时间段内找到作为当前案例经理(“case_mgr”)、“case_mgr_first”和“who_last_call”的用户的接收率。

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

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 我错过了第二个查询的结果,该结果没有出现在第一个 where用户为 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_call”,那就太糟糕了)。最初的 2 个查询每个花费了大约 0.22 秒,而我第一次尝试 JOIN 花费了 0.50 秒。 “case_mgr”字段有索引,但没有“case_mgr_first”。

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

【问题讨论】:

  • @Strawberry 我为这个问题做了很多搜索,发现有些人使用 sqlfiddle。我知道 jsfiddle,但没有意识到有一个 sqlfiddle。所以当我写这个问题的时候,我试着去那里做一个例子,但网站没有工作,尽管在不同的浏览器中重新加载了几次,但它完全是空白的。刚才检查了一下,谢天谢地,它看起来又可以工作了。
  • Sqlfiddle 更人性化,但我更喜欢 rextester。但是,无论哪种方式,这是链接答案中最不重要的部分。
  • @Strawberry 我把它放在一起 - rextester.com/PXQOV60475 。我还在下面的评论中为帕特里克的回答做了一个看起来不错的评论。

标签: mysql join full-outer-join


【解决方案1】:

我想你可以设计一个解决方案,在其中执行 UNION 子选择并在该子选择上再次分组,但这不会很漂亮:

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;

【讨论】:

  • 这看起来效果很好!我知道它是如何工作的。它得到正确的输出,并与带有单个 JOIN 的查询同时运行。如果我想添加另一个字段,它只是 UNION 链中的另一个块和一个额外的列。也许不漂亮,但比在时间和混乱级别上模拟 FULL OUTER JOIN 的其他方法要好得多:) 我编辑了你的答案,将“_first”添加到第三行的末尾。此外,这是一个 rextester.com 示例,您的答案添加到我的原始查询末尾以显示输出-rextester.com/MDS93276
猜你喜欢
  • 2020-02-06
  • 1970-01-01
  • 1970-01-01
  • 2020-07-19
  • 2015-02-07
  • 2015-04-04
  • 2013-03-23
  • 1970-01-01
  • 2021-09-23
相关资源
最近更新 更多