7

我无法在 sql 中获取组函数的输出。以下是表的详细信息

我有 1 个表 表名“checks”有 2 列 pid,cid

 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 PID                                                VARCHAR2(20)
 CID                                                VARCHAR2(20)

以下是可用的行

select * from checks;

PID                  CID
-------------------- --------------------
p1                   c1
p1                   c1
p1                   c2
p1                   c2
p1                   c2
p2                   c1
p2                   c1
p2                   c1
p2                   c1
p2                   c1
p2                   c1
p2                   c2
p2                   c2
p2                   c2
p2                   c2
p2                   c2

P 代表参与者,c 代表类别

问题

我需要知道哪个参与者参与了多个类别中哪个类别参与者参与最多。(对于每个参与者)

预期结果:

pid   cid    count(cid)
---   ---    -----------
p1    c2         3
p2    c1         6
4

5 回答 5

5

假设一个支持窗口函数和 CTE 的数据库系统(你还没有指定,但我怀疑是 Oracle?),我会写:

;With Groups as (
    select pid,cid,COUNT(*) as cnt from checks group by pid,cid
), Ordered as (
    select pid,cid,cnt,
       ROW_NUMBER() OVER (PARTITION BY pid ORDER BY cnt desc) as rn,
       COUNT(*) OVER (PARTITION BY pid) as multi
    from Groups
)
select pid,cid,cnt
from Ordered
where rn = 1 and multi > 1

第一个 CTE ( Groups) 只是找到每个唯一cid,pid组合的计数。第二个 CTE( Ordered) 根据计数为这些结果分配行号 - 最高计数分配的行号为 1。我们还计算为每个 生成的总行数pid

最后,我们选择那些被分配了行号 1(最高计数)并且我们获得了相同的多个结果的行pid

这是一个可供使用的Oracle 小提琴。这是一个SQL Server 版本(感谢 Andriy M 制作了 Oracle 版本)

于 2013-07-18T06:26:22.210 回答
2

一步步:

首先,获取每个(PID, CID). 这很简单:

SELECT
  PID,
  CID,
  COUNT(*) AS cnt
FROM checks
GROUP BY
  PID,
  CID

您会为您的示例获得此结果集:

PID  CID  cnt
---  ---  ---
p1   c1   2
p1   c2   3
p2   c1   6
p2   c2   5

现在,输入COUNT(*) OVER (PARTITION BY PID)以返回每个人的类别数:

SELECT
  PID,
  CID,
  COUNT(*) AS cnt,
  COUNT(*) OVER (PARTITION BY PID) AS cat_cnt
FROM checks
GROUP BY
  PID,
  CID

OVER子句将“普通”聚合函数COUNT()转换为窗口聚合函数。这使得对分组行集而不是源行集进行COUNT(*)操作。因此,在这种情况下 count rows per ,这对我们来说具有每人类别计数的含义。这是更新的结果集:COUNT(*) OVER ...PID

PID  CID  cnt  cnt_cat
---  ---  ---  -------
p1   c1   2    2
p1   c2   3    2
p2   c1   6    2
p2   c2   5    2

剩下的一件事是对cnt每个值进行排名PID。这可能很棘手,因为在最高计数中可能存在平局。如果您总是想要一个单行PID并且完全不关心CID, cnt在平局的情况下哪一行,您可以像这样修改查询:

SELECT
  PID,
  CID,
  COUNT(*) AS cnt,
  COUNT(*) OVER (PARTITION BY PID) AS cat_cnt,
  ROW_NUMBER() OVER (PARTITION BY PID ORDER BY COUNT(*) DESC) AS rn
FROM checks
GROUP BY
  PID,
  CID

这就是结果集的样子:

PID  CID  cnt  cnt_cat  rn
---  ---  ---  -------  --
p1   c1   2    2        2
p1   c2   3    2        1
p2   c1   6    2        1
p2   c2   5    2        2

此时,结果包含产生最终输出所需的所有数据,您只需要过滤cnt_catrn。但是,您不能直接这样做。相反,将最后一个查询用作派生表,无论是WITH表表达式还是“普通”子选择。下面是一个使用示例WITH

WITH grouped AS (
  SELECT
    PID,
    CID,
    COUNT(*) AS cnt,
    COUNT(*) OVER (PARTITION BY PID) AS cat_cnt,
    ROW_NUMBER() OVER (PARTITION BY PID ORDER BY COUNT(*) DESC) AS rn
  FROM checks
  GROUP BY
    PID,
    CID
)
SELECT PID, CID, cnt
FROM grouped
WHERE cat_cnt > 1
  AND rn = 1
;

这是一个 SQL Fiddle 演示(使用 Oracle):http ://sqlfiddle.com/#!4/cd62d/8

为了进一步扩展排名部分,如果您仍想返回单个CID, cntperPID但希望对应确定为“获胜者”的行有更多控制权,则需要在ORDER BY排序函数的子句。例如,您可以修改原始表达式,

ROW_NUMBER() OVER (PARTITION BY PID ORDER BY COUNT(*) DESC) AS rn

有了这个:

ROW_NUMBER() OVER (PARTITION BY PID ORDER BY COUNT(*) DESC, CID) AS rn

即 tie-breaker 是CID,所以在两个或多个CIDs 中,排名靠前的那个获胜。

不过,您可能希望决定返回每个PID. 在这种情况下,使用RANK()DENSE_RANK()代替ROW_NUMBER()(并且没有决胜局),即像这样:

RANK() OVER (PARTITION BY PID ORDER BY COUNT(*) DESC) AS rn
于 2013-07-18T09:17:23.743 回答
1

这会给你一些基本的想法:

在此处输入图像描述

结果如下图。此外,由于 p1 参与了多个类别,因此当我们使用:'group by PID,CID'时,p1 将在不同的行中包含每个新类别

在此处输入图像描述

于 2013-07-18T07:02:12.533 回答
-1
select pid, cid, count
from (
    select pid, cid, count(*) as count
    from checks
    group by pid, cid
    order by count DESC
) as temp
group by pid;

在 MySQL 中也是如此。

于 2013-07-18T06:16:09.760 回答
-1

这是MySQL的解决方案:

SELECT tbl1.pid, tbl1.cid, tbl1.pairCount

FROM        (   SELECT checks.pid, checks.cid, COUNT(*) AS pairCount
                FROM checks
                GROUP BY checks.pid, checks.cid ) AS tbl1

            INNER JOIN 

            (   SELECT checks.pid, checks.cid, COUNT(*) AS pairCount
                FROM checks 
                GROUP BY checks.pid, checks.cid ) AS tbl2   

            ON tbl1.pid=tbl2.pid AND tbl1.cnt > tbl2.cnt

抱歉,我正在使用 2 个子查询,但找不到更好的方法。至少,它有效。小提琴

我不能简单地使用 GROUP BY,因为当使用 GROUP BY 时,为非分组列返回的值是任意的,而不是来自出现 MAX() 值的同一行:MYSQL 在使用 GROUP BY 时显示不正确的行

于 2013-07-18T06:52:34.643 回答