【问题标题】:Display top three values and the sum of all other values显示前三个值和所有其他值的总和
【发布时间】:2013-08-30 19:42:29
【问题描述】:

在我ORDER BY cnt DESC 之后,我的结果是

fld1  cnt
 A     9
 E     8
 D     6
 C     2
 B     2
 F     1

我需要显示前 3 名,其余的汇总为“其他”,如下所示:

fld1  cnt
A      9
E      8
D      6
other  5

已编辑:

感谢大家的意见。如果您看到实际的陈述,也许会有所帮助:

SELECT 
    CAST(u.FA AS VARCHAR(300)) AS FA,
    COUNT(*) AS Total,
    COUNT(CASE WHEN r.RT IN (1,11,12,17) THEN r.RT END) AS Jr,
    COUNT(CASE WHEN r.RT IN (3,4,13) THEN r.RT END) AS Bk,
    COUNT(CASE WHEN r.RT NOT IN (1,11,12,17,3,4,13) THEN r.RT END ) AS Other
FROM R r
    INNER JOIN DB..RTL rt
    ON r.RT = rt.RTID
    INNER JOIN U u
    ON r.UID = u.UID
WHERE rt.LC = 'en' 
GROUP BY CAST(u.FA AS VARCHAR(300))--FA is ntext
ORDER BY Total DESC

生成的结果有 19 条记录。我需要显示前 5 名并将其余的总结为“其他 FA”。我不想用这种语句从一个选择中选择一个选择。我更多的是寻找一些 SQL 函数。也许 ROW_NUMBER 是个好主意,但我不知道在这种情况下如何应用它。

【问题讨论】:

  • 您可能想在这里查看接受的解决方案:stackoverflow.com/questions/14478361/…
  • @PM77-1 不。这个问题不同。
  • 您的结果集有 2 个字段,而您的查询有 5 个,而另一个字段中没有 1 个字段:P 所以我现在不确定两者之间的关系...
  • 如果您将FROM @MyTable x 替换为派生表,则可以调整我的解决方案:FROM (your query without ORDER BY) x 以及其他更改。
  • 结果集也有5个字段。我应该最开始的 2 才是最重要的。我需要 Total 字段中的前 5 名

标签: sql sql-server


【解决方案1】:

可能是这样的:

select top 3 fld1, cnt from mytable
union
select 'Z - Other', sum(cnt) from mytable
where fld1 not in (select top 3 fld1 from mytable order by fld1)
order by fld1

(已更新以包括订单)

【讨论】:

  • 是的,也考虑过这一点,但保持 OP 结构。我也会添加 ORDER BY
  • @KirkBroadhurst:“超级简洁”并不意味着(总是)最好的解决方案。
  • @BogdanSahlean 同意,我也为你的答案投票,但我更喜欢这个。更易于阅读,嵌套查询更少。如果排序很重要,请使用 CTE。
  • @KirkBroadhurst:i-one 表示此解决方案存在一个小问题:所有那些 SELECT TOP(3) ... FROM mytable 都没有 ORDER BY。
  • 好的,我会添加它们以避免混淆
【解决方案2】:

我认为最直接的方法是使用row_number() 来枚举行然后重新聚合它们:

select (case when seqnum <= 3 then fld1 else 'Other' end) as fld1,
       sum(cnt) as cnt
from (select t.*, row_number() over (partition by fld1 order by cnt desc) as seqnum
      from t
     ) t
group by (case when seqnum <= 3 then fld1 else 'Other' end);

您实际上也可以将其作为原始聚合的一部分:

select (case when seqnum <= 3 then fld1 else 'Other' end) as fld1,
       sum(cnt) as cnt
from (select fld1, sum(...) as cnt,
             row_number() over (partition by fld1 order by sum(...) desc) as seqnum
      from t
      group by fld1
     ) t
group by (case when seqnum <= 3 then fld1 else 'Other' end);

编辑(基于修改后的问题):

select (case when seqnum <= 3 then FA else 'Other' end) as FA,
       sum(Total) as Total
from (SELECT CAST(u.FA AS VARCHAR(300)) AS FA,
             COUNT(*) AS Total,
             ROW_NUMBER() over (PARTITION BY CAST(u.FA AS VARCHAR(300)) order by COUNT(*) desc
                               ) as seqnum
      FROM R r
          INNER JOIN DB..RTL rt
          ON r.RT = rt.RTID
          INNER JOIN U u
          ON r.UID = u.UID
      WHERE rt.LC = 'en' 
      GROUP BY CAST(u.FA AS VARCHAR(300))--FA is ntext
     ) t
group by (case when seqnum <= 3 then FA else 'Other' end)
order by max(seqnum) desc;

最后的order by保持记录按总升序排列。

【讨论】:

  • 请看我的编辑。你能帮我把 ROW_NUMBER() 放到那个语句中吗?谢谢。
  • 您的声明中没有任何名为 fld1 的内容
  • 这只是一个示例 :)。 FA 是 fld1,Total 是 cnt
  • 非常感谢!正是我需要的。
【解决方案3】:
DECLARE @MyTable TABLE
(
    fld1 VARCHAR(50) NOT NULL,
    cnt INT NOT NULL
);
INSERT INTO @MyTable (fld1, cnt) 
VALUES 
('A', 9), ('E', 8), ('D', 6),
('C', 2), ('B', 2), ('F', 1);

SELECT  ISNULL(z.new_fld1,'other') AS new_fld1,
        SUM(z.cnt) AS sum_of_cnt
        --,MAX(z.sort_cryteria)
FROM
(
    SELECT  y.cnt, 
            -- I assume that `fld1` column is MANDATORY (NOT NULL) !
            CASE WHEN y.RowNum < 4 THEN fld1 ELSE NULL END AS new_fld1,
            CASE WHEN y.RowNum < 4 THEN y.RowNum ELSE 4 END AS sort_cryteria
    FROM
    (
        SELECT  *, ROW_NUMBER() OVER(ORDER BY x.cnt DESC) AS RowNum
        FROM    @MyTable x
    ) y
) z 
GROUP BY z.new_fld1
ORDER BY MAX(z.sort_cryteria);

结果:

new_fld1 sum_of_cnt  sort_cryteria
-------- ----------- -------------
A        9           1
E        8           2
D        6           3
NULL     5           4

【讨论】:

  • +1,虽然,选择列表中有isnull 的原因是什么? (你可能在case 中做else 'other' end
  • 好问题。 fld1 可以包含一个“其他”值,它可能具有最大的 cnt 值。在这种情况下,如果我使用CASE WHEN RowNum &lt; 4 THEN fld1 ELSE 'other' END,结果将是错误的,因为正确的结果将是(按此顺序):'other'、'E'、'D'、'other'(所有其他 fld1 值 - else 分支)。
【解决方案4】:

你可以试试这样的:

select fld1,cnt from test
where cnt in(select top 3 cnt from test)
union
select 'Other', sum(cnt)from test
where cnt not in (select top 3 cnt from test)
order by cnt desc;

SQLFiddle

【讨论】:

    【解决方案5】:

    我不确定在 SQLServer 中应该如何使用“first”和 skip 子句 但在 firebird 中这是可行的,但我认为可以修改它以在 SQLServer 上运行

        select first 3 p.fld1,p.cnt from Table p
        union
        select t."others",sum(t.cnt )  from (
        select skip 3 'others' as "others",p.cnt from Table p
        ) as t
        group by "others"
    

    【讨论】:

      猜你喜欢
      • 2022-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多