【问题标题】:How To Do Percent/Total in SQL?如何在 SQL 中做百分比/总计?
【发布时间】:2009-07-26 06:58:47
【问题描述】:

我有一组典型的 CUSTOMER/ORDERS 表,我想显示特定客户负责的总销售额百分比。我可以像这样获取系统中的订单总数:

SELECT COUNT(order_id) FROM orders

我可以像这样得到客户的订单总数:

SELECT COUNT(order_id) FROM orders WHERE cust_id = 541

如何将这些组合成一个查询,返回特定客户的销售额百分比?谢谢!

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    MySQL:

    SELECT ROUND(
      100.0 * (
          SUM(IF(cust_id = 541, 1, 0)) / COUNT(order_id)
      ), 1) AS percent_total
    FROM orders;
    

    编辑

    我想如果我注意到 postgres 标记会有所帮助。我认为这是一个 MySQL 问题。

    PostgreSQL:

    SELECT ROUND(
      100.0 * (
          SUM(CASE WHEN cust_id = 541 THEN 1 ELSE 0 END)::numeric / COUNT(order_id)
      ), 1) AS percent_total
    FROM orders;
    

    附:我的 PostgreSQL 生锈了,所以如果 MySQL 查询适用于 PostgreSQL,我想知道 :)

    编辑 2

    我不能强调要警惕下面的 count(*) 建议。您通常希望使用 PostgreSQL 避免这种情况。

    【讨论】:

    • 为什么要避免在 PostgreSQL 中使用 count(*)?
    • 不确定,但也许他的意思是 postgres 每次都会计算计数,这可能是一个昂贵的操作。
    • 至少在 PostgreSql 中,我相信总和需要在除法之前使用 (...)::DECIMAL(5,4) 中的东西进行转换,否则除法将是整数除法,结果将为零。
    • NoChance 是正确的。上面的解决方案对我不起作用(产生 0.0),但这样做:SELECT ROUND(100.0 * (SUM(CASE WHEN cust_id = 541 THEN 1 ELSE 0 END)::DECIMAL / COUNT(order_id)), 1) AS percent_total FROM orders;
    • 假设order_id 对于orders 中的每一行都是非空的(这似乎很可能),那么COUNT(*)COUNT(order_id) 相同。 COUNT(*) 表示“计算输入行数”,而COUNT(foo) 表示“计算foo 为非空的输入行数”。
    【解决方案2】:

    一种解决方案是使用嵌套查询-

    SELECT count(*) / (SELECT count(*) FROM orders)
    FROM orders
    WHERE cust_id = 541
    

    【讨论】:

    • 对于这种微不足道的事情不需要涉及子查询。
    • +1 用于一次性查询,这比 hobodave 的 sumif() 形式更容易/更快地输入和概念化。
    • 不,这是一件非常可怕的事情。在 PostgreSQL 中,count(*) 不像在 MySQL 中那样是 O(1)。此查询将需要两次全表扫描。这是不必要的,并且随着表大小的增加而变得更加糟糕。
    • 我还想补充一点:“更容易打字”一定是做某事的最糟糕的理由之一。除非您参加某种竞争,以在尽可能短的时间内完成尽可能多的代码。
    • 一个更简单的方法是 select cust_id, ( count(*)/(select count(1) from orders ) * 100 ) from orders group by cust_id;
    【解决方案3】:
    select abc.item_name, sum(amount) as total
      from (select a.item_id, d.applicablefrom, a.item_name, a.final_item_status, d.rate, c.item_name as sub_item_name,
                   b.sub_item_qty as itemqty, (b.sub_item_qty * d.rate) as amount 
              from tblitem_master a,
                   tblitem_master c,
                   tblitem_bom_master b,
                   (select rate, applicablefrom, itemid
                      from tblperiodrates
                     where applicablefrom = (select max(applicablefrom) 
                                               from tblperiodrates
                                              where applicablefrom<='2005-5-18')) as d
             where a.item_id = b.item_id
               And b.sub_item_id = c.item_id
               and b.sub_item_id = d.itemid
               and a.final_item_status='f') as abc
     group by abc.item_name
    

    【讨论】:

      【解决方案4】:
      select max([order].customerid) customer_id, count(orderid) customer_orders, (select count(orderid) from [order]) as total_orders,
      100.0 * (count(orderid))/(select count(orderid) from [order])
      from [order] inner join customer
      on [order].customerid = customer.customerid
      group by [order].customerid
      

      为了说明流程,我在最终结果集中包含了比您需要看到的更多的列。 将 count(order_id) 保存在临时变量中会更有效。 我不习惯 postgres,我希望这可以通过最少的修改来工作。

      【讨论】:

      • -1 当我重置每日投票计数时。没有必要为这么简单的事情做 JOIN 和 GROUP BY。
      • -1,那是无效的SQL([]是SQL中的非法字符)
      • @a_horse_with_no_name:在 sql server 中不违法,我响应时没有 postgresql 实例,但在 sql server 上测试正常。对于 2 个表,是的,您需要一个连接。
      • @keni:那么显然微软违反了 SQL 标准。用于保留字的正确字符(在标准 SQL 和几乎所有其他 DBMS 中)是双引号,例如:"order"
      • @a_horse_with_no_name:如果你分享记录标准的链接,我想 +1。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-12
      • 2021-05-07
      • 1970-01-01
      • 1970-01-01
      • 2019-01-26
      相关资源
      最近更新 更多