计算聚合列的百分比

时间:2013-06-20 16:56:01

标签: sql sql-server sql-server-2008-r2

使用以下结果集:

|       DATE | BUSINESS |   COLLEAGUE |   POSITION | HOURS | STANDARDHOURS | COUNTER | OVER16 | OVER32 | OVER48 |
-----------------------------------------------------------------------------------------------------------------
| 2013-01-01 |        a |   bob jones |    analyst |   168 |           168 |       1 |      0 |      0 |      0 |
| 2013-01-01 |        a | cindy jones |  assistant |   184 |           168 |       1 |      1 |      0 |      0 |
| 2013-01-01 |        b |  tim harris | programmer |   200 |           168 |       1 |      1 |      1 |      0 |
| 2013-01-01 |        b |   tom white |    manager |   216 |           168 |       1 |      1 |      1 |      1 |
| 2013-02-01 |        a |   bob jones |    analyst |   176 |           176 |       1 |      0 |      0 |      0 |
| 2013-02-01 |        a | cindy jones |  assistant |   176 |           176 |       1 |      0 |      0 |      0 |
| 2013-02-01 |        b |  tim harris | programmer |   200 |           176 |       1 |      1 |      0 |      0 |
| 2013-02-01 |        b |   tom white |    manager |   216 |           176 |       1 |      1 |      1 |      0 |

使用此查询:

SELECT c.date, 
       c.business, 
       CASE 
         WHEN Sum(c.over16) > 0 THEN ( Sum(c.over16) / Sum(c.counter) ) * 100 
         ELSE 0 
       END AS percOver16, 
       CASE 
         WHEN Sum(c.over32) > 0 THEN ( Sum(c.over32) / Sum(c.counter) ) * 100 
         ELSE 0 
       END AS percOver32, 
       CASE 
         WHEN Sum(c.over48) > 0 THEN ( Sum(c.over48) / Sum(c.counter) ) * 100 
         ELSE 0 
       END AS percOver48 
FROM   (SELECT a.date, 
               a.business, 
               a.colleague, 
               a.position, 
               a.hours, 
               b.standardhours, 
               1   AS counter, 
               CASE 
                 WHEN a.hours >= b.standardhours + 16 THEN 1 
                 ELSE 0 
               END AS over16, 
               CASE 
                 WHEN a.hours >= b.standardhours + 32 THEN 1 
                 ELSE 0 
               END AS over32, 
               CASE 
                 WHEN a.hours >= b.standardhours + 48 THEN 1 
                 ELSE 0 
               END AS over48 
        FROM   colleaguetime a 
               JOIN businesshours b 
                 ON b.date = a.date) c 
GROUP  BY c.date, 
          c.business 

我明白了:

|       DATE | BUSINESS | PERCOVER16 | PERCOVER32 | PERCOVER48 |
----------------------------------------------------------------
| 2013-01-01 |        a |          0 |          0 |          0 |
| 2013-01-01 |        b |        100 |        100 |          0 |
| 2013-02-01 |        a |          0 |          0 |          0 |
| 2013-02-01 |        b |        100 |          0 |          0 |

期望的结果是:

|       DATE | BUSINESS | PERCOVER16 | PERCOVER32 | PERCOVER48 |
----------------------------------------------------------------
| 2013-01-01 |        a |         50 |          0 |          0 |
| 2013-01-01 |        b |        100 |        100 |         50 |
| 2013-02-01 |        a |          0 |          0 |          0 |
| 2013-02-01 |        b |        100 |         50 |          0 |

SQL Fiddle

使用CTE有更简单的方法吗?

3 个答案:

答案 0 :(得分:7)

我相信你遇到的问题是整数除法问题。您希望在进行除法之前将值转换为十进制或浮点格式。这是一种方法:

SELECT c.date, 
       c.business, 
       CASE 
         WHEN Sum(c.over16) > 0 THEN ( Sum(c.over16*1.0) / Sum(c.counter) ) * 100 
         ELSE 0 
       END AS percOver16, 
       CASE 
         WHEN Sum(c.over32) > 0 THEN ( Sum(c.over32*1.0) / Sum(c.counter) ) * 100 
         ELSE 0 
       END AS percOver32, 
       CASE 
         WHEN Sum(c.over48) > 0 THEN ( Sum(c.over48*1.0) / Sum(c.counter) ) * 100 
         ELSE 0 
       END AS percOver48 

编辑:

最简单的替代方法是在子查询中更改计数器的定义:

1.0 as counter,   -- This has a decimal point so it can be used for division

这会将其定义为非整数的数字数据类型。

作为生产代码的一般规则,我希望在发生分割时进行这些转换,以防止意外错误。在某个地方,你或其他人可以看到像1.0 as counter这样的行,并认为“那是愚蠢的。Counter应该是一个整数。”然后你或他或她改变它,然后东西就会破裂。或者有人看到sum(c.counter)并认为“那是愚蠢的。它只能count(*)count(c.counter)

另一方面,对于特殊代码,我可能只是制作计数器1.0

答案 1 :(得分:6)

您可以通过在子查询中使用以下内容来解决此问题:

SELECT a.date, a.business, a.colleague, a.position, a.hours, b.standardHours,
    1 AS counter,
    CASE WHEN a.hours >= b.standardHours + 16
      THEN 1.0 ELSE 0.0 END AS over16,
    CASE WHEN a.hours >= b.standardHours + 32
      THEN 1.0 ELSE 0.0 END AS over32,
    CASE WHEN a.hours >= b.standardHours + 48
      THEN 1.0 ELSE 0.0 END AS over48
FROM colleagueTime a
JOIN businessHours b ON b.date = a.date;

请参阅Demo

不是使用10,而是将值更改为1.0,因此它们是小数而不是整数。

或者正如戈登指出的那样,你可以使用1.0作为计数器值:

SELECT a.date, a.business, a.colleague, a.position, a.hours, b.standardHours,
    1.0 AS counter,
    CASE WHEN a.hours >= b.standardHours + 16
      THEN 1 ELSE 0 END AS over16,
    CASE WHEN a.hours >= b.standardHours + 32
      THEN 1 ELSE 0 END AS over32,
    CASE WHEN a.hours >= b.standardHours + 48
      THEN 1 ELSE 0 END AS over48
FROM colleagueTime a
JOIN businessHours b ON b.date = a.date;

答案 2 :(得分:5)

更改

Sum(c.over16) / Sum(c.counter)

为:

cast(SUM(c.over16) as float) / cast(SUM(c.counter) as float)

(以及所有其他的。)

划分整数会产生整数。你需要先施放浮动。