2016-10-04 84 views
1

我還是新的SQL和讓我周圍的整個子查詢的聚集,顯示了一定的成效頭,並尋找一些建議:SQL聚合函數和排序

的表可能看起來像:

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

關係表:

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

我怎樣才能找到具有至少reward_balance商店? (客戶信息沒有在這一點上需要)

我想:

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

嘗試在內部選擇內添加一個別名到SUM(reward_balance)作爲reward_balance'。 – Iztoksson

+0

嘿Uporabnik003,我根據你的建議編輯它,但現在我得到「列明確定義」的錯誤。 – Ben

+0

我不熟悉Oracle,但嘗試將它命名爲'sum_reward_balance',然後更正主要語句。也許它需要一個唯一的名稱,而不是原來的列名稱。 – Iztoksson

回答

1

解決這個問題一步一步的時間。

我們將假設(並且我們應該檢查一下)至少得到獎勵平衡,這是指與商店相關的所有獎勵平衡的總計。我們不只是尋找具有最低個人獎勵餘額的商店。

首先,獲得每個店鋪的所有個人「酬金平衡」。看起來像查詢需要涉及三個表...

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 

這將讓我們的細節行,與該店鋪相關的個人reward_balance金額沿着每家店鋪,如果有任何。 (我們對這個查詢使用外部連接,因爲我們沒有看到任何商店將與至少一個帳戶相關的任何保證,即使這個用例是真實的,但在更一般的情況下並不總是這樣案例)。

一旦我們有了個人金額,下一步就是爲每個店鋪合計它們。我們可以使用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中不可用)獲得等效的結果。我們還更換了與甲骨文相當於NVL()函數MySQL的IFNULL()函數...

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查詢,這將返回最多一行,即使當兩個或多個店鋪具有相同的「最「total_balance_balance。

如果我們想要返回tot_reward_balance最低的所有商店,我們需要採取稍微不同的方法。


構建查詢的最佳方法是逐步細化;在這種情況下,首先獲得每個商店的所有個人獎勵金額。下一步是將個人reward_amount彙總爲一個總數。接下來的步驟是挑出總獎勵金額最低的行。

+0

感謝您的驚人建議spencer7593。我真的很感激所花費的時間來演示構建步驟以及包括NULL對象等事件。 – Ben

+0

@Ben:其他人可以一舉剔除多行SQL語句。我不是那麼聰明。我必須一次採取一步,並驗證每一步。 「迭代查詢開發」(就像我所說的那樣)是我採取的方法,它對我很有用。我認爲它使我能夠發現問題,並沿途解決問題。當我遇到問題時,我可以向後退一步,並加以解決。 (我沒有足夠的智能去調試一個「爲什麼這個龐大的SQL語句沒有返回我期望的結果」而沒有對它進行反向工程,我只是「正向工程」我的SQL語句YMMV – spencer7593

+0

您可能會發現我的SQL的格式語句很奇怪,這是一種適用於我的格式,例如,我可以在IDE中輕鬆地突出顯示內聯視圖查詢並執行該操作,我可以很容易地看到內聯視圖查詢返回兩列。我不在乎格式化,它不關心額外的中斷,額外的空白區域,參數排隊,我用這種方式格式化語句,以便我可以解讀它,當我不得不返回它時。 – spencer7593

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 
+0

嘿艾米特,感謝您的幫助,對不起,我是堆棧溢出新,並沒有注意到自動標記,但我使用Oracle而不是SQL服務器對不起。我已經刪除了不正確的標籤。 – Ben