当前位置: 首页 > 面试题库 >

选择超过总值百分比的行的子集

阎声
2023-03-14
问题内容

我有一张与客户,用户和收入类似的表格(实际上成千上万条记录):

Customer   User    Revenue
001        James   500
002        James   750
003        James   450
004        Sarah   100
005        Sarah   500
006        Sarah   150
007        Sarah   600
008        James   150
009        James   100

我想做的是只回报支出最高的客户,这些客户占用户总收入的80%。

要手动执行此操作,我将按詹姆斯的客户的收入对其进行排序,计算出总计的百分比和运行的总计百分比,然后仅返回记录,直到运行的总计达到80%:

Customer    User    Revenue     % of total  Running Total %
002         James   750         0.38        0.38 
001         James   500         0.26        0.64 
003         James   450         0.23        0.87  <- Greater than 80%, last record
008         James   150         0.08        0.95 
009         James   100         0.05        1.00

我已经尝试过使用CTE,但到目前为止还是空白。有没有办法通过单个查询而不是在Excel工作表中手动执行此操作?


问题答案:

SQL Server 2012+ 只要

您可以使用windowed SUM

WITH cte AS
(
   SELECT *,
          1.0 * Revenue/SUM(Revenue) OVER(PARTITION BY [User]) AS percentile,
          1.0 * SUM(Revenue) OVER(PARTITION BY [User] ORDER BY [Revenue] DESC)
                /SUM(Revenue) OVER(PARTITION BY [User]) AS running_percentile
   FROM tab
)
SELECT *
FROM cte 
WHERE running_percentile <= 0.8;

**[LiveDemo](http://rextester.com/IDYCVJ26861)**

SQL Server 2008:

WITH cte AS
(
    SELECT *, ROW_NUMBER() OVER(PARTITION BY [User] ORDER BY Revenue DESC) AS rn
    FROM t    
), cte2 AS
(
    SELECT c.Customer, c.[User], c.[Revenue]
           ,percentile         = 1.0 * Revenue / NULLIF(c3.s,0)
           ,running_percentile = 1.0 * c2.s    / NULLIF(c3.s,0)
    FROM cte c
    CROSS APPLY
         (SELECT SUM(Revenue) AS s
          FROM cte c2
          WHERE c.[User] = c2.[User]
            AND c2.rn <= c.rn) c2
    CROSS APPLY
         (SELECT SUM(Revenue) AS s
          FROM cte c2
          WHERE c.[User] = c2.[User]) AS c3
) 
SELECT *
FROM cte2
WHERE running_percentile <= 0.8;

**[LiveDemo2](http://rextester.com/TUQ82944)**

输出:

╔══════════╦═══════╦═════════╦════════════════╦════════════════════╗
║ Customer ║ User  ║ Revenue ║   percentile   ║ running_percentile ║
╠══════════╬═══════╬═════════╬════════════════╬════════════════════╣
║        2 ║ James ║     750 ║ 0,384615384615 ║ 0,384615384615     ║
║        1 ║ James ║     500 ║ 0,256410256410 ║ 0,641025641025     ║
║        7 ║ Sarah ║     600 ║ 0,444444444444 ║ 0,444444444444     ║
╚══════════╩═══════╩═════════╩════════════════╩════════════════════╝

编辑2:

看上去差不多,唯一的麻烦是缺少最后一行,詹姆斯的第三行让他超过0.80,但需要包括在内。

WITH cte AS
(
    SELECT *, ROW_NUMBER() OVER(PARTITION BY [User] ORDER BY Revenue DESC) AS rn
    FROM t    
), cte2 AS
(
    SELECT c.Customer, c.[User], c.[Revenue]
           ,percentile         = 1.0 * Revenue / NULLIF(c3.s,0)
           ,running_percentile = 1.0 * c2.s    / NULLIF(c3.s,0)
    FROM cte c
    CROSS APPLY
         (SELECT SUM(Revenue) AS s
          FROM cte c2
          WHERE c.[User] = c2.[User]
            AND c2.rn <= c.rn) c2
    CROSS APPLY
         (SELECT SUM(Revenue) AS s
          FROM cte c2
          WHERE c.[User] = c2.[User]) AS c3
) 
SELECT a.*
FROM cte2 a
CROSS APPLY (SELECT MIN(running_percentile) AS rp
             FROM cte2
             WHERE running_percentile >= 0.8
               AND cte2.[User] = a.[User]) AS s
WHERE a.running_percentile <= s.rp;
LiveDemo3

输出:

╔══════════╦═══════╦═════════╦════════════════╦════════════════════╗
║ Customer ║ User  ║ Revenue ║   percentile   ║ running_percentile ║
╠══════════╬═══════╬═════════╬════════════════╬════════════════════╣
║        2 ║ James ║     750 ║ 0,384615384615 ║ 0,384615384615     ║
║        1 ║ James ║     500 ║ 0,256410256410 ║ 0,641025641025     ║
║        3 ║ James ║     450 ║ 0,230769230769 ║ 0,871794871794     ║
║        7 ║ Sarah ║     600 ║ 0,444444444444 ║ 0,444444444444     ║
║        5 ║ Sarah ║     500 ║ 0,370370370370 ║ 0,814814814814     ║
╚══════════╩═══════╩═════════╩════════════════╩════════════════════╝

看起来很完美,已翻译成我的大桌子并返回了我需要的东西,花了5分钟时间完成它,但仍然无法跟随你所做的事情!

SQL Server 2008不支持OVER()子句中的所有内容,但支持ROW_NUMBER。

首先,CTE只是计算组内的位置:

╔═══════════╦════════╦══════════╦════╗
║ Customer  ║ User   ║ Revenue  ║ rn ║
╠═══════════╬════════╬══════════╬════╣
║        2  ║ James  ║     750  ║  1 ║
║        1  ║ James  ║     500  ║  2 ║
║        3  ║ James  ║     450  ║  3 ║
║        8  ║ James  ║     150  ║  4 ║
║        9  ║ James  ║     100  ║  5 ║
║        7  ║ Sarah  ║     600  ║  1 ║
║        5  ║ Sarah  ║     500  ║  2 ║
║        6  ║ Sarah  ║     150  ║  3 ║
║        4  ║ Sarah  ║     100  ║  4 ║
╚═══════════╩════════╩══════════╩════╝

第二个CTE:

c2 子查询根据以下排名计算运行总计 ROW_NUMBER
c3 计算每位用户的总和
在最终查询中,s子查询找到了running总数超过80%的最低总数。

编辑3:

使用ROW_NUMBER实际上是多余的。

WITH cte AS
(
    SELECT c.Customer, c.[User], c.[Revenue]
           ,percentile         = 1.0 * Revenue / NULLIF(c3.s,0)
           ,running_percentile = 1.0 * c2.s    / NULLIF(c3.s,0)
    FROM t c
    CROSS APPLY
         (SELECT SUM(Revenue) AS s
          FROM t c2
          WHERE c.[User] = c2.[User]
            AND c2.Revenue >= c.Revenue) c2
    CROSS APPLY
         (SELECT SUM(Revenue) AS s
          FROM t c2
          WHERE c.[User] = c2.[User]) AS c3
) 
SELECT a.*
FROM cte a
CROSS APPLY (SELECT MIN(running_percentile) AS rp
             FROM cte c2
             WHERE running_percentile >= 0.8
               AND c2.[User] = a.[User]) AS s
WHERE a.running_percentile <= s.rp
ORDER BY [User], Revenue DESC;


 类似资料:
  • 问题内容: 我想使用第二列(art_count)仅显示那些包含总art_count的X%的行。 我的资料: 到目前为止我的查询: 使用SUM进行了尝试,但未成功。 问题答案: 您需要为插入一个值

  • 我需要根据该值被选择的百分比概率选择一个值。例如: 时间增量值a的10% 时间增量值b的20% 时间增量值c的30% 时间增量值d的40% 百分比加起来总是正好100% 我遇到过几种像这样的解决方案,但我确定它们不可能是正确的。以下是使用上述解决方案构建的示例程序: 输出: 预期输出: 我相信我需要使用某种算法将百分比转换为从0到99的刻度,以便随机数生成器可以准确地选择一个值。不过,我想不出如何

  • 问题内容: 我有一个MSSQL表存储,该存储在表中具有以下列: 有人可以帮我进行SQL查询,以产生占雇员总数(NumEmployees)30%的顶级商店(storeID)吗? 问题答案: WITH cte AS (SELECT storeid, numemployees, ( numemployees * 100 ) / SUM(numemployees) OVER (PARTITION BY 1

  • 问题内容: 我有一个带有PHP脚本的站点,此脚本在返回由JavaScript文件访问的数据的内部有一个SQL查询。该数据是一个庞大的航班数据列表,我需要能够在指定的任何给定日期中随机选择(比如说)全部航班的40%。为了论证,让我们这样说: 我知道要获得随机使用的行数,理想情况下我想说,但这行不通。 编辑: 问题答案: 您可以记录所有内容,然后按以下方式计算所需的内容:

  • 问题内容: 假设我有一个这样的表: AB 3 CD 1 EF 2 GH 4 最后一列的总和为10,我希望最大的总和至少为总价值的60%。因此,在这种情况下,它将返回 GH 4 AB 3 最高上升到70%,但如果仅选择第一个值,则最高上升到40%。即使可能会有组合返回正好60%的组合,我们也要取最大的数字。 因此,我想我知道如何从最大到最小对值进行排序,以及如何对所有值求和,但是我不知道如何只取总和

  • 问题内容: 我有这个查询: 它给了我每一行的计数。现在,我想添加第三列,这将给我带来帮助。 我怎样才能做到这一点? 问题答案: 您可以使用子查询来做到这一点: 或带有变量: 在两个示例中,我都将count(*)转换为实数,以避免整数除法类型问题。 希望这对约翰有帮助