2017-08-31 85 views
4

DB - Oracle。所有約束被省略。在sql中結合等級和總和

create table customer (cid number(10), cname varchar(50)); 

create table exercise (eid number(10), ecode varchar(2), score number(2)); 

-- mapping table 
create table customer_exercise (cid number(10), eid number(10), cnt number(10)) 

Customer table 

cid cname 
100 e1 
200 e2 
300 e3 
400 e4 

Exercise table 

eid ecode score 
1  c1  5 
2  c2  10 
3  c3  6 
4  c4  3 

Customer_Exercise 

cid eid count 
100 1 2 
200 2 5 
100 2 3 
300 4 10 

SQL檢索總數 -

SELECT c.cid 
    ,e.eid 
    ,COALESCE(SUM(ce.cnt), 0) AS total_cnt 
FROM customer c 
    CROSS JOIN exercise e 
    LEFT JOIN customer_exercise ce 
     ON  ce.cid = c.cid 
      AND ce.eid = e.eid 
WHERE c.cid IN (100, 200, 300) 
     AND e.eid IN (1, 2) 
GROUP BY c.cid, e.eid 
ORDER by c.cid 

結果 -

c.cid e.eid total_cnt 
100 1   2 
100 2   3 
200 1   0 
200 2   5 
300 1   0 
300 2   0 

SQL來計算排名爲每個客戶爲

select cid , RANK() OVER (ORDER BY sum(total_score) desc) as rank from 
(
SELECT c.cid as cid 
    ,e.eid 
    ,COALESCE(SUM(ce.cnt), 0) AS total_cnt 
    , COALESCE(SUM(ce.cnt), 0) * e.score as total_score 
FROM customer c 
    CROSS JOIN exercise e 
    LEFT JOIN customer_exercise ce 
     ON  ce.cid = c.cid 
      AND ce.eid = e.eid 
WHERE  c.cid IN (100, 200, 300) 
    AND e.eid IN (1, 2) 
GROUP BY c.cid, e.eid, e.score 
) 
GROUP BY cid 
ORDER BY rank 

結果 -

c.cid rank 
    200 1 
    100 2 
    300 3 

是否有可能使用一個查詢而不是上述兩個查詢得到結果集?我期待將上述兩個查詢的結果合併爲一個。預期結果發佈如下。

預期的結果 - 三代表的

c.cid e.eid total_cnt rank 
200  1  0   1 
200  2  5   1 
100  1  2   2 
100  2  3   2 
300  1  0   3 
300  2  0   3 

回答

4

的連接是使用了兩次 - 一次顯示計數和再次計算的行列。第一個版本不彙總,而第二個版本(並且在彙總後計算分析等級)。這是WITH子句的完美應用:在分解子查詢(CTE,WITH子句)中執行三個表的連接並使用它兩次。

with 
    j (cid, eid, score, cnt) as (
     select c.cid, e.eid, e.score, ce.cnt 
     from customer c cross join exercise e 
       left outer join customer_exercise ce on c.cid = ce.cid 
                and e.eid = ce.eid 
     where c.cid in (100, 200, 300) 
     and e.eid in (1, 2) 
    ) 
select j.cid, j.eid, nvl(j.cnt, 0) as total_count, r.rnk 
from j left join (select cid, 
           rank() over (order by sum(cnt*score) desc nulls last) as rnk 
        from  j 
        group by cid 
        ) r 
        on j.cid = r.cid 
order by rnk, eid 
; 
0

你可以使用SUM(...)OVER(...)來計算這個數值爲每一個客戶的總成績,然後排名。

SELECT cid, eid, SUM(cnt) AS total_cnt, DENSE_RANK() OVER (ORDER BY cid, total_score DESC) AS rank 
FROM 
    (
     select c.cid, e.eid, e.score, ce.cnt, SUM(ce.cnt * ce.score) OVER (PARTITION BY c.cid) AS total_score 
     from customer c cross join exercise e 
       left outer join customer_exercise ce on c.cid = ce.cid 
                and e.eid = ce.eid 
     where c.cid in (100, 200, 300) 
     and e.eid in (1, 2) 
    ) data 
GROUP BY cid, eid, total_score 
ORDER BY rank, eid 

我創建了一個演示here

+0

不要使用像'r ank「作爲列名/別名!使用'rnk'或'rk'或類似的; 'rank'是一個函數的名字。然後:在這個上下文中使用的'dense_rank()'將起作用,如果行列不同的話。然而,如果有兩個'cid'並列爲最高分,那麼使用'rank'而不是'dense_rank'的OP查詢將產生排名1,1,3。'dense_rank'將產生1,1,1 ,1,2,2(由於交叉連接,每個dense_rank重複)。這與OP的要求不一樣。 – mathguy

+0

謝謝@mathguy –

-1

您還可以使用multiple CTE + SUM + DENSE_RANK()專爲MS SQL數據庫查詢

;WITH CTE_CUST AS 
(
SELECT cust.cid 
     ,exe.eid 
     ,exe.score 
     ,SUM(ISNULL(cust_exe.cnt,0)) AS total_cnt 
FROM customer cust 
     CROSS JOIN 
     exercise exe 
     LEFT JOIN 
     customer_exercise cust_exe 
      ON cust_exe.cid = cust.cid 
       AND cust_exe.eid = exe.eid 
GROUP BY cust.cid, exe.eid,exe.score 
) 
, CTE_RANK AS 
(
SELECT cid, DENSE_RANK() OVER (order by (SUM(ISNULL((total_cnt * cte.score),0))) DESC) rank_score 
FROM CTE_CUST cte 
GROUP BY cid 
) 
SELECT cust.cid, cust.eid, cust.total_cnt as 'count', rnk.rank_score 
FROM CTE_RANK AS rnk 
     JOIN 
     CTE_CUST AS cust 
      ON rnk.cid = cust.cid 
WHERE cust.cid IN (100, 200, 300) 
      AND cust.eid IN (1, 2) 
ORDER BY rank_score 
+0

'dense_rank'與'rank'不一樣,對吧?對於明確標記爲'oracle'的問題,給出SQL Server的答案與回答「你如何用法語說母語*」和「我不懂法語,但西班牙語是* madre *」一樣有幫助。因爲這個原因,我們常常會告訴人們「我不知道法語,但是西班牙語是......」,如果我們希望網站有幫助,就必須停止。 – mathguy

+0

@mathguy:是'dense_rank'與'rank'不一樣,但我們也可以用'dense_rank'來實現這個,這就是我想說的,我已經給了他一個不同的方法來處理同樣的問題,並且還有一件事,我知道他已經要求Oracle DB了,但是如果有些Sql人員指的是相同的話,那麼他們也會對他們有所幫助,如果我的查詢出錯了,那麼你可以說其他的不是 –

+0

不,你可以無論是排名還是dense_rank都無法達到預期的效果。至於其他方面,它在你身上。不要忽略人們的標籤,讓我們看看你做得如何。 – mathguy

0

我會的解析函數兩個層面做到這一點:

select cid, eid, score, cnt, 
     dense_rank() over (order by coalece(total_score, 0) desc) as rnk 
from (select c.cid, e.eid, e.score, ce.cnt, 
      sum(e.score) over (partition by c.cid) as total_score 
     from customer c cross join 
      exercise e left outer join 
      customer_exercise ce 
      on c.cid = ce.cid and e.eid = ce.eid 
     where c.cid in (100, 200, 300) and e.eid in (1, 2) 
    ) ce; 
+0

這是行不通的:它會將等級1分配兩次(因爲同一'cid'有兩行,顯然兩者總分相同),然後分配給第二高總分的等級將是3,不是2.而分配給第三高總分的等級將是5,而不是3。 – mathguy