SQL 找出元组的平均数量(既不是最大值也不是最小值)

SQL find average number of tuples (neither max nor min)

假设我有两个表 PEOPLEWORK。我想找到所有 work 的最大和最小数量 people.

示例:

PEOPLE

ID NAME   WORK
1  Mark   Programmer
2  Sonia  Singer
3  Jack   Programmer
4  Mirco  Welder
5  Jeff   Welder
6  Tom    Welder

WORK

ID  WORK
1   Programmer
2   Singer
3   Welder

结果应该是:

Programmer

这样的MySQL query怎么写??

谢谢大家


你必须认为这是一种脑筋急转弯。你肯定会找到一组 3 个元素:

1 Maximum represented by Welder job done by Mirco, Jeff and Tom
2 Medium represented by Programmer done by Mark and Jack who is the Job I would like to find
3 Minimum represented by Singer done only from Sonia

谢谢

此查询适用于支持 CTE 的 SQL 版本(例如 SQL Server、PostgreSQL、MySQL 8+):

WITH counts AS
(SELECT WORK, COUNT(*) AS num
 FROM people
 GROUP BY WORK),
minmax AS 
(SELECT MIN(num) AS min, MAX(num) AS max
 FROM counts)
SELECT WORK
FROM counts c
JOIN minmax m ON c.num != m.min AND c.num != m.max

输出:

WORK
Programmer

Demo on dbfiddle

这是一种使用 window 函数的方法:

  • 首先使用聚合查询计算每个作品的人数
  • 然后按人数升序和降序排列作品
  • 最后,过滤掉顶部和底部的记录

查询:

SELECT work
FROM (
    SELECT 
        work,
        ROW_NUMBER() OVER(ORDER BY cnt) rn_asc,
        ROW_NUMBER() OVER(ORDER BY cnt DESC) rn_desc
    FROM (
        SELECT work, COUNT(*) cnt FROM people GROUP BY work
    ) x
) x WHERE rn_asc != 1 AND rn_desc != 1

Demo on DB Fiddle:

| work       |
| ---------- |
| Programmer |

注意:就此而言,您无需查询 work table 即可获得预期结果,因为所有相关信息都可在 people.

另一种方式。使用子查询查找最小和最大计数,并将其与每个工作的计数连接到另一个子查询。然后左连接并消除匹配最小或最大计数的行。

declare @people table (id int, name varchar(20), work varchar(50))
insert into @people
values (1,  'Mark'  , 'Programmer'),
(2,  'Sonia'  ,'Singer'),
(3,  'Jack',   'Programmer'),
(4,  'Mirco',  'Welder'),
(5,  'Jeff',   'Welder'),
(6,  'Tom',    'Welder')

select a.work
from ( -- A subquery that returns the count of each work
   select work, count(*) cnt
   from @people
   group by work
) a
left outer join ( -- a subquery that returns the min and max counts
   select min(cnt) mincount, max(cnt) maxcount
   from (
      select count(*) cnt
      from @people
      group by work
   ) b
) c
   on a.cnt = c.mincount
   or a.cnt = c.maxcount
where c.mincount is null

如果您想在没有 window 功能的情况下执行此操作:

select w.*
from work w
where w.work not in (select p.work
                     from people p
                     group by p.work
                     order by count(*) asc
                     fetch first 1 row only
                    ) and
      w.work not in (select p.work
                     from people p
                     group by p.work
                     order by count(*) desc
                     fetch first 1 row only
                    );

请注意,如果出现重复,这只会考虑一行 minimum/maximum,因此可以返回并列。