使用 MYSQL Group By 获取最受欢迎的值

Posted

技术标签:

【中文标题】使用 MYSQL Group By 获取最受欢迎的值【英文标题】:using MYSQL Group By to get the most popular value 【发布时间】:2021-10-18 12:13:38 【问题描述】:

我正在使用https://www.w3schools.com/mysql/trymysql.asp?filename=trysql_func_mysql_concat 练习 MYSQL,它有一个模拟数据库供我练习,我正在尝试使用 GROUP BY 命令我正在尝试将所有员工与他们的所有销售额分组并确定他们的姓名、他们的销售额和他们卖得最多的产品。我设法获得了他们的名称和销售额,但没有获得产品名称。我知道用 group by 提取信息很困难,我尝试过使用子查询。有没有办法获取信息。 我的查询如下。

SELECT 
    CONCAT_WS(' ',
            Employees.FirstName,
            Employees.LastName) AS 'Employee name',
    COUNT(*) AS 'Num of sales'
FROM
    Orders
        INNER JOIN
    Employees ON Orders.EmployeeID = Employees.EmployeeID
        INNER JOIN
    OrderDetails ON OrderDetails.OrderID = Orders.OrderID
        INNER JOIN
    Products ON Products.ProductID = OrderDetails.ProductID
GROUP BY Orders.EmployeeID
ORDER BY COUNT(*) DESC;

这里的意思是获取订单,根据订单employeeid加入员工,根据订单id加入订单明细,在订单明细中根据产品id加入产品信息,然后按员工id分组并按顺序排序员工的销售额。

SELECT 
  concat_ws(' ',
           Employees.FirstName,
           Employees.LastName) as 'Employee name',
  count(*) as 'Num of sales',
  (
    SELECT Products.ProductName 
    FROM Orders 
    INNER JOIN Employees ON Orders.EmployeeID = Employees.EmployeeID 
    INNER JOIN OrderDetails ON OrderDetails.OrderID = Orders.OrderID 
    INNER JOIN Products ON Products.ProductID = OrderDetails.ProductID 
    GROUP BY Orders.EmployeeID 
    ORDER BY count(Products.ProductName) desc
    LIMIT 1
  ) as 'Product Name'
FROM Orders 
INNER JOIN Employees ON Orders.EmployeeID = Employees.EmployeeID 
INNER JOIN OrderDetails ON OrderDetails.OrderID = Orders.OrderID 
INNER JOIN Products ON Products.ProductID = OrderDetails.ProductID 
GROUP BY Orders.EmployeeID 
ORDER BY count(*) desc;

以上是我尝试对解决方案使用子查询。

【问题讨论】:

你遇到困难的原因是这困难 :-) 你认为“一切都在那里,员工、他们的订单、订购的产品;所以为什么我不能轻易找到最畅销的产品吗?”。这是因为 MySQL 缺少一个聚合函数。您正在寻找的称为统计模式,例如 Oralce 有一个函数 STATS_MODE 用于此。但 MySQL 没有。 解决此问题的一种方法是在 select 子句的子查询中再次选择所有这些内容,然后按产品计数降序排列,并使用 LIMIT 获取第一行。 我确实尝试了子查询,但我只得到了相同的产品名称,我认为这是因为我的查询有缺陷并且正在选择任何产品名称或正在为一个选择最受欢迎的产品员工,然后将其显示给所有员工。 我添加了我对使用子查询的解决方案的尝试。 我已经发布了一个答案 :-) 附带说明:单引号分隔字符串文字。对于别名,您应该改用双引号。在某些情况下,当混淆两者时,您可能会得到意想不到的结果。对于别名,我会使用无论如何都不必引用的名称(employee_name、num_of_sales、...)。 【参考方案1】:

这很丑,因为 w3school 仍然使用 mysql 5.7

就个人而言,您应该在某个数据库中安装自己的服务器并在那里测试它,在 mysql 工作台中,您可以有许多查询选项卡,您可以在其中测试查询,直到您获得“正确”的结果。

SELECT 
    CONCAT_WS(' ',
            Employees.FirstName,
            Employees.LastName) AS 'Employee name',
    COUNT(*) AS 'Num of sales',
    tn.ProductName
FROM
    Orders
        INNER JOIN
    Employees ON Orders.EmployeeID = Employees.EmployeeID
        INNER JOIN
    OrderDetails ON OrderDetails.OrderID = Orders.OrderID
        INNER JOIN
    Products ON Products.ProductID = OrderDetails.ProductID
 INNEr JOIN 
    (SELECT EmployeeID, p.ProductName
    FROM (SELECT IF (@Eid = EmployeeID ,@rn := @rn +1, @rn := 1) rn,ProductID,  sumamount
    , @Eid := EmployeeID  as EmployeeID
    FROM
    (
SELECT
    EmployeeID,ProductID, SUM(Quantity) sumamount
    FROM Orders o INNER JOIN OrderDetails od ON od.OrderID = o.OrderID,(SELECT @Eid := 0, @rn := 0) t1
    GROUP BY EmployeeID,ProductID
    ORDER BY EmployeeID,sumamount DESC ) t2 ) t3
    INNER JOIN Products p ON t3.ProductID = p.ProductID
    WHERE rn= 1) tn 
    ON Orders.EmployeeID = tn.EmployeeID
GROUP BY Orders.EmployeeID
ORDER BY COUNT(*) DESC;

【讨论】:

谢谢你,我只是看看你做了什么并试图理解它,我还没有太多使用变量的经验。 内部选择对数据进行分组并计算其余部分的总和是 riwnnumber 以获得最高数量,但您可以轻松地将其扩展到前 3 个示例 @Bob James:在查询中计算和递增的变量很难看,有时难以理解,有时容易出错。它们在这里用于模拟 MySQL 5.7 中缺少的功能。没有必要学习如何使用它们。正如 nbk 暗示的那样,获得 MySQL 8 会更好,并以标准的 SQL 方式学习。【参考方案2】:

在您的第二个查询中,您试图获取员工最常销售的产品。但是那个子查询有两个错误:

    子查询无效。您按员工分组,但选择一个产品。哪个产品?一名员工可以销售许多不同的产品。 MySQL 应该在这里引发语法错误,就像我所知道的所有其他 DBMS 一样。但是你处于作弊模式。 MySQL 允许不正确的聚合查询,并在所有无法以其他方式选择的列上静默应用ANY_VALUE。因此,您选择了ANY_VALUE(Products.ProductName),即 DBMS 任意选择的产品。退出作弊模式SET sql_mode = 'ONLY_FULL_GROUP_BY';。 然后,您不会将子查询与主查询相关联。因此,在选择该行时,例如,员工#123,您的子查询仍然为所有员工选择数据,以便选择其产品之一。由于这与主查询中的员工无关,因此它可能也会为您选择的所有其他员工选择相同的产品。

下面是查询的样子:

SELECT 
  concat_ws(' ', e.FirstName, e.LastName) as "Employee name",
  count(*) as "Num of sales",
  (
    SELECT p2.ProductName 
    FROM Orders o2
    INNER JOIN OrderDetails od2 ON od2.OrderID = o2.OrderID 
    INNER JOIN Products p2 ON p2.ProductID = od2.ProductID 
    WHERE o2.EmployeeID = o.EmployeeID
    GROUP BY p2.ProductID
    ORDER BY count(*) DESC
    LIMIT 1
  ) as "Product Name"
FROM Orders o 
INNER JOIN Employees e ON o.EmployeeID = e.EmployeeID 
INNER JOIN OrderDetails od ON od.OrderID = o.OrderID 
GROUP BY o.EmployeeID 
ORDER BY count(*) desc;

演示:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=f35e96764d454a4032d7778b550fc6b4

免责声明:当员工最常销售一种以上的产品时(例如 500 x 产品 A、500 x 产品 B、200 x 产品 C),那么其中一个(示例中的 A 或 B)会被任意挑选用于员工。

【讨论】:

以上是关于使用 MYSQL Group By 获取最受欢迎的值的主要内容,如果未能解决你的问题,请参考以下文章

MySQL:ORDER BY 表达式?

MySQL Query - 使用 group-by 时获取丢失的记录

使用 MySQL 通过 JOIN 获取 GROUP BY 中的 SUM

mysql在group by之后如何获取每一组中id最大的那一行

mysql在group by之后如何获取每一组中id最大的那一行

在 MySql Godaddy VPS Cpanel 中使用 sql_mode=only_full_group_by 获取 MYSQL 错误