SQL聚合函数和排序

时间:2016-10-04 05:33:21

标签: sql oracle aggregate

我还是SQL的新手,并且围绕整个子查询聚合来展示一些结果,并且正在寻找一些建议:

表格可能类似于:

Customer: (custID,  name, address)
Account: (accountID, reward_balance)
Shop: (shopID, name, address)

关系表:

Holds (custID*, accountID*)
With (accountID*, shopID*)

如何找到奖励平衡最少的商店? (此时不需要客户信息)

我试过了:

SELECT accountID AS ACCOUNT_ID, shopID AS SHOP_ID, MIN(reward_balance) AS LOWEST_BALANCE
FROM Account, Shop, With
WHERE With.accountID = Account.accountID
AND With.shopID=Shop.shopID
GROUP BY
Account.accountID,
Shop.shopID
ORDER BY MIN(reward_balance);

这是以无意的方式工作:

ACCOUNT_ID | SHOP_ID | LOWEST_BALANCE
 1         |   1     |   10
 2         |   2     |   40
 3         |   3     |   100
 4         |   4     |   1000
 5         |   4     |   5000

正如您所见,Shop_ID 4实际上有6000(1000 + 5000)的余额,因为有两个客户注册了它。我认为我需要根据他们的余额来衡量商店的最低余额,并从低位显示它。

我一直在尝试在显示之前聚合数据,但这是我解开的地方:

SELECT shopID AS SHOP_ID, MIN(reward_balance) AS LOWEST_BALANCE
FROM (SELECT accountID, shopID, SUM(reward_balance) 
     FROM Account, Shop, With
     WHERE 
         With.accountID = Account.accountID
         AND With.shopID=Shop.shopID
         GROUP BY
             Account.accountID,
             Shop.shopID;

当我运行类似于此语句的内容时,我收到无效的标识符错误。

Error at Command Line : 1 Column : 24
Error report -
SQL Error: ORA-00904: "REWARD_BALANCE": invalid identifier
00904. 00000 -  "%s: invalid identifier"

所以我认为我的加入条件可能不正确,汇总排序不正确,非常感谢任何一般建议。

感谢您阅读冗长!

2 个答案:

答案 0 :(得分:1)

一步一步地解决这个问题。

我们将假设(并且我们应该检查一下)至少使用reward_balance,它指的是与商店相关的所有reward_balance的。而且我们不只是寻找具有最低个人奖励余额的商店。

首先,获得每个商店的所有个人“reward_balance”。看起来查询需要涉及三个表...

SELECT s.shop_id
     , a.reward_balance
  FROM `shop` s
  LEFT
  JOIN `with` w
    ON w.shop_id = s.shop_id 
  LEFT
  JOIN `account` a
    ON a.account_id = w.account_id

这将为我们提供详细信息行,每个商店以及与商店相关的个人奖励平衡金额(如果有的话)。 (我们正在使用外部联接进行此查询,因为我们没有看到任何商店与至少一个帐户相关的保证。即使这个用例也是如此,在更一般的情况下并不总是这样。情况)。

一旦我们获得了单独的金额,下一步就是为每个商店计算总额。我们可以使用GROUP BY子句和SUM()聚合来实现这一点。

SELECT s.shop_id
     , SUM(a.reward_balance) AS tot_reward_balance
  FROM `shop` s
  LEFT
  JOIN `with` w
    ON w.shop_id = s.shop_id 
  LEFT
  JOIN `account` a
    ON a.account_id = w.account_id
 GROUP BY s.shop_id

此时,使用MySQL,我们可以添加ORDER BY子句以按tot_reward_balance的升序排列行,如果我们只想返回单行,则添加LIMIT 1子句。当tot_reward_balance为NULL时,我们也可以处理这种情况,指定零代替NULL。

SELECT s.shop_id
     , IFNULL(SUM(a.reward_balance),0) AS tot_reward_balance
  FROM `shop` s
  LEFT
  JOIN `with` w
    ON w.shop_id = s.shop_id 
  LEFT
  JOIN `account` a
    ON a.account_id = w.account_id
 GROUP BY s.shop_id
 ORDER BY tot_reward_amount ASC, s.shop_id ASC
 LIMIT 1

如果有两个(或更多)商店具有相同的最小值tot_reward_amount,则此查询仅返回其中一个商店。

Oracle没有像MySQL这样的LIMIT子句,但我们可以使用分析函数(在MySQL中不可用)获得相同的结果。我们还将MySQL IFNULL()函数替换为Oracle等效的NVL()函数......

SELECT v.shop_id
     , v.tot_reward_balance
     , ROW_NUMBER() OVER (ORDER BY v.tot_reward_balance ASC, v.shop_id ASC) AS rn
  FROM ( 
         SELECT s.shop_id
              , NVL(SUM(a.reward_balance),0) AS tot_reward_balance
           FROM shop s
           LEFT
           JOIN with w
             ON w.shop_id = s.shop_id 
           LEFT
           JOIN account a
             ON a.account_id = w.account_id
          GROUP BY s.shop_id
       ) v
HAVING rn = 1

与MySQL查询一样,即使两个或两个以上的商店总共拥有相同的“最少”的reward_balance,也会返回最多一行。

如果我们想要返回所有具有最低tot_reward_balance的商店,我们需要采取略微不同的方法。

构建查询的最佳方法是逐步完善;在这种情况下,首先获得每个商店的所有个人reward_amount。下一步是将个人reward_amount聚合为总数。接下来的步骤是挑选总奖励金额最低的行。

答案 1 :(得分:0)

在SQL Server中,您可以尝试使用CTE:

  ;with cte_minvalue as
    (
     select rank() over (order by Sum_Balance) as RowRank,
     ShopId,
     Sum_Balance
      from (SELECT Shop.shopID, SUM(reward_balance) AS Sum_Balance 
         FROM 
             With
             JOIN Shop ON With.ShopId = Shop.ShopId
             JOIN Account ON With.AccountId = Account.AccountId
         GROUP BY
            Shop.shopID)ShopSum
      )
      select ShopId, Sum_Balance from cte_minvalue where RowRank = 1