2011-12-06 128 views
4

我的數據是這樣的:SQL有條件的分組和總結

|cat |subcat |amount| 
--------------------- 
|A |1  |123 | 
|A |2  |456 | 
|B |1  |222 | 
|B |2  |333 | 

在第一種情況下,我需要通過貓SUBCAT總結。簡單:

SELECT cat, subcat, sum(amount) FROM data GROUP BY cat, subcat 

接下來,我有一個更復雜的要求,對於某些貓,金額應該「推」到一個給定的子貓。這可以被存儲在另一個config表:

|cat |subcat| 
------------- 
|B |1  | 

這告訴我,所有cat='B'行,量應爲subcat=1處理。此外,其中cat='B' AND subcat <> 1的數量應該報告爲零。換句話說,我需要的結果是:

|cat |subcat|amount| 
|A |1  |123 | 
|A |2  |456 | 
|B |1  |555 | 
|B |2  |0  | 

我無法更新我的數據表。當然,我可以在一個proc中使用SELECT ... INTO並修復數據,但是我想知道是否可以在一次打擊中完成。

我可以得到相當親近:

SELECT data.cat, 
    ISNULL(config.subcat, data.subcat), 
    SUM(amount) 
FROM data 
    LEFT OUTER JOIN config ON (data.cat = config.cat) 
GROUP BY data.cat, ISNULL(config.subcat, data.subcat) 

...但失敗我的第二個規定,以顯示cat:B, subcat:2爲零。

可能嗎?

我使用Sybase IQ 12.5(即舊的T-SQL,但是有case聲明,我懷疑可能是有用的)

+1

我不太關注這部分:「這告訴我,對於所有貓='B'行,金額應該被視爲一個subcat = 1此外,其中cat ='B 'AND subcat <> 1的金額應報告爲零。「從第一句話來看,[B,2]應該被認爲是222而不是333,但第二句,[B,2]應該被視爲0而不是333.你可以顯示一個表格,應該在規則應用時處理? – Glenn

回答

0

我有點困惑的要求,但我認爲這是你想要的。

SELECT d.cat, 
     d.subcat, 
     SUM(CASE 
      WHEN c.subcat IS NULL OR c.subcat = d.subcat 
      THEN d.amount 
      ELSE 0 
     END) as Amount 
FROM @Data d 
    LEFT OUTER JOIN @Config c ON (d.cat = c.cat) 
GROUP BY d.cat, d.subcat 
ORDER BY d.cat 

例在這裏 - http://data.stackexchange.com/stackoverflow/q/120507/

讓我知道這是不是你所追求的。

+0

這樣做的結果有'B | 2 | 0'(好)行,但有'B | 1 | 222',而我想'B | 1 | 555' – Rob

0

我正在使用tsql,這裏是我的代碼。它很醜,但很有用。實際上,我喜歡你非常接近的方法(如果你不堅持顯示B2 = 0)。

SELECT A.cat, 
     A.subcat, 
     CASE WHEN B.IsConfig = 0 THEN A.amount 
      WHEN B.IsConfig = 1 AND C.cat IS NULL THEN 0 
      ELSE B.amount 
     END AS amount 
FROM data A 
INNER JOIN 
(
    SELECT B1.cat, B1.amount, CASE WHEN C1.cat IS NULL THEN 0 ELSE 1 END AS IsConfig 
    FROM 
    (
     SELECT cat, SUM(amount) amount 
     FROM data 
     GROUP BY cat 
    ) B1 LEFT OUTER JOIN config C1 ON B1.cat = C1.cat 
) B ON A.cat = B.cat 
LEFT OUTER JOIN config C ON A.cat = C.cat AND A.subcat = C.subcat 

---我不能對別人,所以我在這裏添加我的問題發表評論---

相比,我的代碼與他人使用的執行計劃,我的查詢費用爲46%。這是否意味着它更高效?或者它只是取決於:)

1

這是我想出來的。

SELECT cat, subcat, sum(amount) 
FROM 
(
    SELECT d.cat, 
     d.subcat, 
     CASE WHEN c.subcat <> d.subcat THEN 0 ELSE amount END amount 
    FROM data d 
     LEFT OUTER JOIN config c ON (d.cat = c.cat) 
    UNION  
    SELECT d.cat, 
     ISNULL(c.subcat, d.subcat), 
     amount 
    FROM data d 
     LEFT OUTER JOIN config c ON (d.cat = c.cat) 
    WHERE c.subcat <> d.subcat 
) AS data2 
GROUP BY cat, subcat 

由於它使用了派生表與工會,和我的實際數據集是一個比我的問題給了大很多,我覺得SELECT ... INTO隨後的更新實際上可能是更好的性能接近!

1

你需要一個連接Data -> Config -> Data翻譯的B2至B1,然後UNION,要與Case語句,然後SUM和GROUP BY一個SELECT容易

SELECT 
    t.CAT, 
    t.SUBCAT, 
    SUM(t.AMOUNT) AMOUNT 
FROM 
(
SELECT d.cat, 
     d.subcat, 
     CASE 
     WHEN c.subcat IS NULL 
       OR c.subcat = d.subcat THEN d.amount 
     ELSE 0 
     END AS amount 
FROM data d 
     LEFT JOIN config c 
     ON d.cat = c.cat 

UNION ALL 

SELECT d.cat, 
     d.subcat, 
     d2.amount 
FROM data d 
     INNER JOIN config c 
     ON (d.cat = c.cat) 
     INNER JOIN data d2 
     ON c.cat = d2.cat 
      AND c.subcat <> d2.subcat 
      AND c.subcat = d.subcat 
) t 
GROUP BY 
    cat, 
    subcat 
ORDER BY 
    cat, 
    subcat 
​ 

你可以看到一個工作示例在此data.se query

注意我增加了第三個「B」值來測試在有多個捲起SUBCAT

一個使用WITH和ROLLUP子句(這是在某些版本的Sybase的我不知道支持的另一種方法其中)

with g as ( 
    SELECT 

     d.cat, 
     d.subcat, 
     c.subcat config_subcat, 
     sum(amount) amount, 
     GROUPING(c.subcat) subcatgroup 
    FROM data d 
    LEFT JOIN config c 
    ON d.cat = c.cat 

    GROUP BY 
     d.cat, 
     d.subcat, 
     c.subcat with rollup 
) 

SELECT 
    g.cat, 
    g.subcat, 
    case when g.config_subcat is null then g.amount 
    WHEN g.subcat = g.config_subcat THEN g2.amount 
    ELSE 0 end amount 
FROM g 

    LEFT JOIN g g2 
    ON g.cat = g2.cat and g2.subcatgroup= 1 
    and g.subcat is not null and g2.subcat is null 

WHERE g.subcatgroup= 0​​ 

其中可瀏覽該data.se query

+0

如果'config'包含一個未找到的子類別'數據'爲一個特定的類別?您的解決方案似乎沒有解釋這一點。 –

+0

不,它不是一個要求?我想這可以通過一個完整的外部連接加上一些煤炭來解決 –

+0

不,OP沒有提到這個,所以當然,假設這種情況要麼是不可能的,要麼是在其他地方被說明是完全正確的。除此之外,您的解決方案可以正常工作。 –

0

計算SUM(amount)在派生表中的「配置」中引用的所有「貓」 s,則匹配你的「數據」表項爲適當:

SELECT data.cat, 
      data.subcat, 
      CASE 
      WHEN dt.subcat IS NULL  -- no "config" entry for cat 
       THEN data.amount 
      WHEN dt.subcat = data.subcat -- "config" for cat and subcat 
       THEN dt.total 
      ELSE 0      -- "config" for cat not subcat 
      END AS amount 
    FROM data 
LEFT JOIN ( SELECT config.cat, 
        config.subcat, 
        SUM(data.amount) AS total 
       FROM config 
       JOIN data USING (cat) 
      GROUP BY 1, 2) dt 
      USING (cat); 

+-----+--------+--------+ 
| cat | subcat | amount | 
+-----+--------+--------+ 
| A |  1 | 123 | 
| A |  2 | 456 | 
| B |  1 | 555 | 
| B |  2 |  0 | 
+-----+--------+--------+ 
4 rows in set (0.00 sec) 
0

這有點類似於您的解決方案,但UNION僅用於建立類別和子類別的列表。該列表然後與另一個派生表連接,這與您的UNION的正確部分基本相同。這裏是:

SELECT s.cat, s.subcat, ISNULL(SUM(d.amount), 0) 
FROM (
    SELECT cat, subcat FROM data 
    UNION 
    SELECT cat, subcat FROM config 
) s 
    LEFT JOIN (
    SELECT 
     d.cat, 
     subcat = ISNULL(c.subcat, d.subcat), 
     d.amount 
    FROM data d 
     LEFT JOIN config c ON d.cat = c.cat 
) d ON s.cat = d.cat AND s.subcat = d.subcat 
GROUP BY s.cat, s.subcat