10

编辑:我正在使用 MySQL,我发现另一个帖子有同样的问题,但它在 Postgres 中;我需要 MySQL。

获取 SQL 中另一列的每个值的最常见值

在广泛搜索此站点和其他站点后,我提出了这个问题,但没有找到符合我预期的结果。

我有一张人员表(recordid、personid、transactionid)和一个事务表(transactionid、rating)。我需要一个可以返回每个人最常见的评分的 SQL 语句。

我目前有这个 SQL 语句,它返回指定人员 ID 的最常见评级。它有效,也许它可以帮助其他人。

SELECT transactionTable.rating as MostCommonRating 
FROM personTable, transactionTable 
WHERE personTable.transactionid = transactionTable.transactionid 
AND personTable.personid = 1
GROUP BY transactionTable.rating 
ORDER BY COUNT(transactionTable.rating) desc 
LIMIT 1

但是,我需要一个语句来执行上述语句对 personTable 中的每个 personid 所做的事情。

我的尝试如下;但是,它使我的 MySQL 服务器超时。

SELECT personid AS pid, 
(SELECT transactionTable.rating as MostCommonRating 
FROM personTable, transactionTable 
WHERE personTable.transactionid = transactionTable.transactionid 
AND personTable.personid = pid
GROUP BY transactionTable.rating 
ORDER BY COUNT(transactionTable.rating) desc 
LIMIT 1)
FROM persontable
GROUP BY personid

您能给我的任何帮助将不胜感激。谢谢。

PERSONTABLE

RecordID,   PersonID,   TransactionID
1,      Adam,       1
2,      Adam,       2
3,      Adam,       3
4,      Ben,        1
5,      Ben,        3
6,      Ben,        4
7,      Caitlin,    4
8,      Caitlin,    5
9,      Caitlin,    1

TRANSACTIONTABLE

TransactionID,  Rating
1       Good
2       Bad
3       Good
4       Average
5       Average

我正在搜索的 SQL 语句的输出是:

输出

PersonID,   MostCommonRating
Adam        Good
Ben         Good
Caitlin     Average
4

3 回答 3

27

初步评论

请学习使用显式 JOIN 表示法,而不是旧的(1992 年之前)隐式连接表示法。

老款式:

SELECT transactionTable.rating as MostCommonRating 
FROM personTable, transactionTable 
WHERE personTable.transactionid = transactionTable.transactionid 
AND personTable.personid = 1
GROUP BY transactionTable.rating 
ORDER BY COUNT(transactionTable.rating) desc 
LIMIT 1

首选风格:

SELECT transactionTable.rating AS MostCommonRating 
  FROM personTable
  JOIN transactionTable 
    ON personTable.transactionid = transactionTable.transactionid 
 WHERE personTable.personid = 1
 GROUP BY transactionTable.rating 
 ORDER BY COUNT(transactionTable.rating) desc 
 LIMIT 1

每个 JOIN 都需要一个 ON 条件。

此外,personID数据中的值是字符串,而不是数字,因此您需要编写

 WHERE personTable.personid = "Ben"

例如,让查询在显示的表上工作。


主要答案

您正在寻找聚合的聚合:在这种情况下,是计数的最大值。因此,任何通用解决方案都将涉及 MAX 和 COUNT。您不能将 MAX 直接应用于 COUNT,但可以将 MAX 应用于子查询中的列,而该列恰好是 COUNT。

使用测试驱动的查询设计 - TDQD 构建查询。

选择人员和交易评级

SELECT p.PersonID, t.Rating, t.TransactionID
  FROM PersonTable AS p
  JOIN TransactionTable AS t
    ON p.TransactionID = t.TransactionID

选择人员、评分和评分出现次数

SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
  FROM PersonTable AS p
  JOIN TransactionTable AS t
    ON p.TransactionID = t.TransactionID
 GROUP BY p.PersonID, t.Rating

这个结果将成为一个子查询。

查找此人获得任何评分的最大次数

SELECT s.PersonID, MAX(s.RatingCount)
  FROM (SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
          FROM PersonTable AS p
          JOIN TransactionTable AS t
            ON p.TransactionID = t.TransactionID
         GROUP BY p.PersonID, t.Rating
       ) AS s
 GROUP BY s.PersonID

现在我们知道每个人的最大计数是多少。

所需结果

为了得到结果,我们需要从子查询中选择具有最大计数的行。请注意,如果某人有 2 个好和 2 个差的评级(并且 2 是该人的同一类型的最大评级数),则将为该人显示两条记录。

SELECT s.PersonID, s.Rating
  FROM (SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
          FROM PersonTable AS p
          JOIN TransactionTable AS t
            ON p.TransactionID = t.TransactionID
         GROUP BY p.PersonID, t.Rating
       ) AS s
  JOIN (SELECT s.PersonID, MAX(s.RatingCount) AS MaxRatingCount
          FROM (SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
                  FROM PersonTable AS p
                  JOIN TransactionTable AS t
                    ON p.TransactionID = t.TransactionID
                 GROUP BY p.PersonID, t.Rating
               ) AS s
         GROUP BY s.PersonID
       ) AS m
    ON s.PersonID = m.PersonID AND s.RatingCount = m.MaxRatingCount

如果您也想要实际的评分计数,那么很容易选择。

这是一段相当复杂的 SQL。我不想尝试从头开始写。确实,我可能不会打扰;我会逐步开发它,或多或少如图所示。但是因为我们在将子查询用于更大的表达式之前已经对它们进行了调试,所以我们可以对答案充满信心。

WITH子句

请注意,标准 SQL 提供了一个 WITH 子句作为 SELECT 语句的前缀,命名一个子查询。(它也可以用于递归查询,但我们在这里不需要。)

WITH RatingList AS
     (SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
        FROM PersonTable AS p
        JOIN TransactionTable AS t
          ON p.TransactionID = t.TransactionID
       GROUP BY p.PersonID, t.Rating
     )
SELECT s.PersonID, s.Rating
  FROM RatingList AS s
  JOIN (SELECT s.PersonID, MAX(s.RatingCount) AS MaxRatingCount
          FROM RatingList AS s
         GROUP BY s.PersonID
       ) AS m
    ON s.PersonID = m.PersonID AND s.RatingCount = m.MaxRatingCount

这样写起来更简单。不幸的是,MySQL 还不支持 WITH 子句。


上面的 SQL 现在已经针对在 Mac OS X 10.7.4 上运行的 IBM Informix Dynamic Server 11.70.FC2 进行了测试。该测试暴露了初步评论中诊断的问题。主要答案的 SQL 可以正常工作,无需更改。

于 2012-09-16T14:42:53.920 回答
3

max这是对 MySQL 中的聚合函数对 varchars 进行词法排序(以及对整数/浮点数的预期数字排序)这一事实的一种有点粗俗的滥用:

SELECT 
  PersonID, 
  substring(max(concat(lpad(c, 20, '0'), Rating)), 21) AS MostFrequentRating 
FROM (
    SELECT PersonID, Rating, count(*) c 
    FROM PERSONTABLE INNER JOIN TRANSACTIONTABLE USING(TransactionID) 
    GROUP BY PersonID, Rating
) AS grouped_ratings 
GROUP BY PersonID;

这给出了所需的:

+----------+--------------------+
| PersonID | MostFrequentRating |
+----------+--------------------+
| Adam     | Good               |
| Ben      | Good               |
| Caitlin  | Average            |
+----------+--------------------+

(请注意,如果每个人有多种模式,它将选择具有最高字母条目的模式,因此 - 几乎是随机的 - 好而不是坏,坏而不是平均)

您应该能够max通过检查以下内容来查看正在运行的内容:

SELECT PersonID, Rating, count(*) c, concat(lpad(count(*), 20, '0'), Rating) as LexicalMaxMe 
FROM PERSONTABLE INNER JOIN TRANSACTIONTABLE USING(TransactionID) 
GROUP BY PersonID, Rating
ORDER BY PersonID, c DESC;

哪个输出:

+----------+---------+---+-----------------------------+
| PersonID | Rating  | c | LexicalMaxMe                |
+----------+---------+---+-----------------------------+
| Adam     | Good    | 2 | 00000000000000000002Good    |
| Adam     | Bad     | 1 | 00000000000000000001Bad     |
| Ben      | Good    | 2 | 00000000000000000002Good    |
| Ben      | Average | 1 | 00000000000000000001Average |
| Caitlin  | Average | 2 | 00000000000000000002Average |
| Caitlin  | Good    | 1 | 00000000000000000001Good    |    
+----------+---------+---+-----------------------------+
于 2019-06-18T13:59:02.080 回答
0

对于使用 Microsoft SQL Server 的任何人:您可以创建自定义聚合函数以获得最常见的值。Ahmed Tarek Hasan 的这篇博文的示例 2 描述了如何做到这一点:

http://developmentsimplyput.blogspot.nl/2013/03/creating-sql-custom-user-defined.html

于 2017-12-20T12:26:22.100 回答