MySQL如何根据列中的状态查找百分比字段填充

时间:2017-11-10 00:41:17

标签: mysql sql

我做了一些研究,但似乎没有任何帮助我的个案

我有一张桌子(有40列和数百万行)

FirstName| LaseName | State |...100+ other columns|
aaa      | bbb      | CA
ccc      | ddd      | NY
abc      | null     | CA
null     | ggg      | AL
...150 million rows

我需要一个很长的查询来返回类似下面的内容

State | field     | # of state pupulation | # of rows    in state | % state population
__________________________________________________________________________
AL    | firstName |  0                    |         1             | 0%
      | lastName  |  1                    |                       | 100%
__________________________________________________________________________
CA    | firstName |  2                    |         2             | 100%
      | lastname  |  1                    |                       | 50%
__________________________________________________________________________
NY    | firstName |  1                    |         1             | 100%
      | lastname  |  1                    |                       | 100%

这仅供内部使用,因此只要我能获得所需的数字,格式/顺序就不重要了

请注意,%是通过(状态= AL,CA等中的非空数#/状态= AL,CA等的总记录数)来计算的

而不是(非空的#/所有行的#)

我是sql的新手,我不知道该怎么做

1 个答案:

答案 0 :(得分:0)

这是一种可能的查询模式。为了获得百分比,我们将选择列表中的第三个表达式除以第四个表达式。 (要在单个SELECT中执行此操作,我们必须重复这些表达式,由除法运算符分隔。

SELECT c.state 

     , f.field

     , CASE f.field
       WHEN 'firstname'  THEN c.cnt_fn_nn
       WHEN 'lastname'   THEN c.cnt_ln_nn
       WHEN 'somecol'    THEN c.cnt_sc_nn
       ELSE NULL
       END        AS `# populated` 

     , c.cnt_tot  AS `# rows`

     , CASE f.field
       WHEN 'firstname'  THEN c.cnt_fn_nn
       WHEN 'lastname'   THEN c.cnt_ln_nn
       WHEN 'somecol'    THEN c.cnt_sc_nn
       ELSE NULL
       END
     / c.cnt_tot * 100.0 AS `pct`

  FROM ( SELECT 'firstname' AS `field`
          UNION ALL SELECT 'lastname'
          UNION ALL SELECT 'somecol'
       ) f
 CROSS
  JOIN ( SELECT a.state
              , SUM(1) AS `cnt_tot`
              , SUM(a.first_name IS NOT NULL) AS `cnt_fn_nn`
              , SUM(a.last_name  IS NOT NULL) AS `cnt_ln_nn`
              , SUM(a.somecol    IS NOT NULL) AS `cnt_sc_nn`
           FROM atable a
          GROUP BY a.state
       ) c
 ORDER
    BY c.state
     , f.field

注意:

内联视图(派生表)f返回我们要在第二列中显示的field值。

内联视图(派生表)c通过state获取“计数”。

GROUP BY子句会折叠state具有相同值的所有行,因此我们会为state的每个不同值返回一行。

对每一行计算表达式a.first_name IS NOT NULL,作为布尔值,并为每一行返回FALSE(0)或TRUE(1)。我们可以使用SUM()聚合函数来总计1和0。这使我们计算first_name的非null值的行数。

SUM(1)会让我们和COUNT(*)一样。这是每个state的总行数。

我们对两个内联视图执行CROSS JOIN操作,以生成笛卡尔(交叉)乘积。 f的每一行都与c的每一行匹配。如果我们有50个状态,来自c的50行和来自f的3行,则会得到总共50 * 3行。

搜索到的CASE表达式是一种SQL语言...

 if field = 'firstname'   then return cnt_fn_nn 
 elsif field = 'lastname' then return cnt_ln_nn
 ... 

我们使用它来“挑选”我们想要在每一行返回的计数,在firstname字段的行上返回名字不为空的计数。

这可以扩展,使用somecol复制行并将其替换为atable中的有效列名,并为SELECT列表中的每个表达式分配唯一的列别名AS foo

此查询未向我们提供的是state中未显示的任何atable值。要获得零计数,我们需要state的其他来源。

此查询不会抑制状态或# rows in state的重复值。这些值在每一行都会返回,即使它们是重复的。

要让那些被压抑的人变得蠢,但我们可以做到。要在每个state的“第一行”上获取它,我们需要知道哪个field将是第一个。我们可以在外部查询的SELECT列表中的第一个和第四个表达式中进行条件测试。在开始使用它之前,首先使查询正常工作。 (重复值的抑制最好在处理结果的客户端中处理。但是如果规范要返回那个确切的结果集,那么就可以这样做......我很畏缩......)

SELECT IF(f.field='firstname',c.state,'') AS `state` 

     , f.field

     , CASE f.field
       WHEN 'firstname'  THEN c.cnt_fn_nn
       WHEN 'lastname'   THEN c.cnt_ln_nn
       WHEN 'somecol'    THEN c.cnt_sc_nn
       ELSE NULL
       END        AS `# populated` 

     , IF(f.field='firstname',c.cnt_tot,NULL) AS `# rows`