【问题标题】:mysql for percentage between rowsmysql用于行之间的百分比
【发布时间】:2016-05-03 14:55:36
【问题描述】:

我有一些看起来像这样的 sql:

SELECT 
stageName,
count(*) as `count`

FROM x2production.contact_stages
WHERE FROM_UNIXTIME(createDate) between  '2016-05-01' AND DATE_ADD('2016-08-31', INTERVAL 1 DAY) 
    AND (stageName = 'DI-Whatever' OR stageName = 'DI-Quote' or stageName = 'DI-Meeting')
Group by stageName
Order by field(stageName, 'DI-Quote', 'DI-Meeting', 'DI-Whatever')

这会生成一个如下所示的表格:

+-------------+-------+
|  stageName  | count |
+-------------+-------+
| DI-quote    |  1230 |
| DI-Meeting  |   985 |
| DI-Whatever |   325 |
+-------------+-------+

问题:

我想要从一行到下一行的百分比。例如 DI 会议与 DI 报价的百分比。数学将是 100*985/1230 = 80.0%

所以最后表格看起来像这样:

+-------------+-------+------+
|  stageName  | count | perc |
+-------------+-------+------+
| DI-quote    |  1230 | 0    |
| DI-Meeting  |   985 | 80.0 |
| DI-Whatever |   325 | 32.9 |
+-------------+-------+------+

有没有办法在 mysql 中做到这一点?

这是一个用于混淆数据的 SQL 小提琴:http://sqlfiddle.com/#!9/61398/1

【问题讨论】:

  • 通过 order by 子句或者你有一个唯一的列来指定顺序?
  • @sagi 你的权利,错字已修正。
  • 是的,这是非常可行的。再说一次,你已经一个月没来了,所以我会等。
  • @Drew 哦?请告诉:)。
  • 当然,将返回这三行的 sqlfiddle 放在一起,我会这样做

标签: mysql


【解决方案1】:

查询

select stageName,count,if(rownum=1,0,round(count/toDivideBy*100,3)) as percent 
from 
(   select stageName,count,greatest(@rn:=@rn+1,0) as rownum, 
    coalesce(if(@rn=1,count,@prev),null) as toDivideBy,
    @prev:=count as dummy2 
    from 
    (   SELECT  
        stageName, 
        count(*) as `count` 
        FROM Table1 
        WHERE FROM_UNIXTIME(createDate) between  '2016-05-01' AND DATE_ADD('2016-08-31', INTERVAL 1 DAY)  
            AND (stageName = 'DI-Underwriting' OR stageName = 'DI-Quote' or stageName = 'DI-Meeting') 
        Group by stageName 
        Order by field(stageName, 'DI-Quote', 'DI-Meeting', 'DI-Underwriting') 
    ) xDerived1 
    cross join (select @rn:=0,@prev:=-1) as xParams1 
) xDerived2; 

结果

+-----------------+-------+---------+
| stageName       | count | percent |
+-----------------+-------+---------+
| DI-Quote        |    16 |       0 |
| DI-Meeting      |    13 |  81.250 |
| DI-Underwriting |     4 |  30.769 |
+-----------------+-------+---------+

注意,您需要 0 作为第一行的百分比。这很容易变成 100。

cross join 引入变量以供使用并初始化它们。 greatestcoalesce 用于变量使用的安全性,正如 article 中的详细说明,以及来自 MySQL 手册页 Operator Precedence 的线索。派生表名称就是这样:每个派生表都需要一个名称。

如果您不遵守那些参考文章中的原则,那么使用变量是不安全的。我并不是说我做到了,但安全始终是我关注的焦点。

变量的赋值需要遵循一种安全的形式,比如@rn变量被设置在像greatestleast这样的函数内部。我们知道@rn 总是大于0。所以我们使用greatest 函数来强制查询。与coalesce 相同的技巧,永远不会发生null,并且:= 在其后面的列中具有较低的优先级。也就是最后一个:@prev:=,跟在coalesce之后。

这样,在该选择行中的其他列尝试使用其值之前设置一个变量。

因此,仅仅获得预期的结果并不意味着您安全地完成了它并且它将与您的真实数据一起使用。

【讨论】:

  • 这很棒。您的脚本和@Jorge Campos 脚本都有效,但您的解释很好。这真的让我学到了很多东西。快速提问:如果我使用像 postgres/redshift 这样具有更好分析功能的数据库,这个查询会有什么不同?
  • 我不能为他们说话。许多具有 CTE 功能,如 mysql 必须用变量模仿的 rownum。而且我不能谈论他们可以做出的优先模型或假设。就像 Gordon 在顶部显示的一样(引自 mysql 手册)stackoverflow.com/a/22388942
  • 在postgres中你确实有LAG功能,查询会容易很多。
【解决方案2】:

您需要使用LAG 函数,因为MySQL 不支持它,您必须以这种方式模仿它:

select stageName,
       cnt,
       IF(valBefore is null,0,((100*cnt)/valBefore)) as perc
  from (SELECT tb.stageName, 
               tb.cnt,
               @ct AS valBefore,
              (@ct := cnt) 
         FROM (SELECT stageName,
                      count(*) as cnt
                 FROM Table1,
                      (SELECT @_stage = NULL,
                              @ct := NULL) vars
                WHERE FROM_UNIXTIME(createDate) between  '2016-05-01' 
                                                AND DATE_ADD('2016-08-31', INTERVAL 1 DAY) 
                  AND stageName in ('DI-Underwriting', 'DI-Quote', 'DI-Meeting')
                Group by stageName
                Order by field(stageName, 'DI-Quote', 'DI-Meeting', 'DI-Underwriting')                 
              ) tb
        WHERE (CASE WHEN @_stage IS NULL OR @_stage <> tb.stageName
                    THEN @ct := NULL 
                    ELSE NULL END IS NULL)  
       ) as final

在这里查看它的工作原理:http://sqlfiddle.com/#!9/61398/35

编辑我实际上已经对其进行了编辑以删除不必要的步骤(子查询)

【讨论】:

    猜你喜欢
    • 2021-01-11
    • 2021-02-04
    • 1970-01-01
    • 2014-04-06
    • 2019-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-25
    相关资源
    最近更新 更多