2014-02-17 65 views
7

我2012年的MS-SQL服務器上運行,並有其他領域與年齡表USER,性別和銷售表的銷售記錄。TSQL計算各種%,基於不同的領域

我目前在計算銷售排行榜顯示,從而來舉個例子返回此列表根據他們的頂級銷售各種銷售代表通過他們的頂級銷售下令銷售人員的名單。某處在列表的中間,我們有托馬斯先生這讓我們說是#4。

我現在的任務是要顯示托馬斯如何比較具有相同年齡的他,銷售人員他還如何與銷售代表有相同性別作爲他進行比較。該計算將返回與上述總體列表不同的結果。

我理想中的存儲過程將接受1個參數(用戶ID),並返回下面的單個記錄 值:OverallPosition,OverallPositionTotalCount,AgePosition,AgeTotalCount,GenderPosition,GenderTotalCount

數據樣本:

CREATE TABLE dbo.User 
( 
    UserId int NOT NULL IDENTITY (1, 1), 
    Name nvarchar(50) NOT NULL, 
    Age int NULL, 
    Gender nvarchar(10) NULL 
)  

1, James, 30, 'male' 
2, Monica, 27, 'female' 
3, Paul, 30, 'male' 
4, Thomas, 30, 'male' 
5, Mike, 22, 'male' 
6, Sabrina, 30, 'female' 


CREATE TABLE dbo.Sales 
( 
    SalesId int NOT NULL IDENTITY (1, 1), 
    UserId int NOT NULL, 
    TotalSale int NOT NULL 
) ON [PRIMARY] 

1, 1, $900,000 
2, 1, $1,000,000 
3, 2, $900,000 
4, 2, $400,000 
5, 3, $750,000 
6, 3, $300,000 
7, 4, $875,000 
8, 5, $700,000 
9, 5, $1,200,000 
10, 6, $850,000 

銷售排行榜名單

SELECT u.UserId, u.Name, MAX(s.TotalSale) as TopSale, Count(*) OVER() AS TotalCount 
FROM User u 
    INNER JOIN Sales s on s.UserId = u.UserId 
GROUP BY u.UserID, u.Name 
ORDER BY TopSale DESC 
OFFSET (@PageIndexSelected) * @PageCountSelected ROWS 
FETCH NEXT @PageCountSelected ROWS ONLY 

理想的計算結果
由於托馬斯(用戶ID 4)年齡30和「男性」,他的數據應該是這樣的

OverallPosition = 4; OverallPositionTotalCount = 6 (i.e 4 out of 6)  
$1,200,000 Mike 
$1,000,000 James 
$900,000  Monica 
$875,000  Thomas 
$850,000  Sabrina 
$750,000  Paul 

AgePosition = 2; AgeTotalCount = 4 (i.e. 2 out of 4) 
$1,000,000 James 
$875,000  Thomas 
$850,000  Sabrina 
$750,000  Paul 

GenderPosition = 3; GenderTotalCount = 4 (i.e 3 out of 4) 
$1,200,000 Mike 
$1,000,000 James 
$875,000  Thomas 
$750,000  Paul 

注意
預期的結果是隻對OverallPosition值,OverallPositionTotalCount,AgePosition,AgeTotalCount,GenderPosition,GenderTotalCount爲單個用戶(存儲過程將接收的用戶ID爲PARAM),而不是實際列表。

預期回報
OverallPosition = 4,
OverallPositionTotalCount = 6,
AgePosition = 2,
AgeTotalCount = 4,
GenderPosition = 3,
GenderTotalCount = 4

正如我所說在我的評論中,我真的不知道如何解決這個問題。我希望有人願意幫助!

+0

你能後,你有這麼遠的代碼? – Kermit

+1

我沒有任何代碼,因爲我不知道如何處理這個問題。這就是我發佈這個問題的原因。順便說一句,你是誰點擊-1? –

+2

是的。一旦您通過嘗試或研究工作更新了您的問題,我會將其刪除。 – Kermit

回答

5

第一CTE得到每個人的最大的銷售。第二個使用窗口函數rank()count()與適當的over()子句來計算位置和總計。

with C1 as 
(
    select U.UserId, 
     U.Gender, 
     U.Age, 
     max(S.TotalSale) as TotalSale 
    from dbo.[User] as U 
    inner join dbo.Sales as S 
     on U.UserId = S.UserId 
    group by U.UserId, 
      U.Gender, 
      U.Age 
), C2 as 
(
    select C1.UserId, 
     C1.TotalSale, 
     rank() over(order by C1.TotalSale desc) as OverallPosition, 
     rank() over(partition by C1.Age order by C1.TotalSale desc) as AgePosition, 
     rank() over(partition by C1.Gender order by C1.TotalSale desc) as GenderPosition, 
     count(*) over() as OverallPositionTotalCount, 
     count(*) over(partition by C1.Age) as AgeTotalCount, 
     count(*) over(partition by C1.Gender) as GenderTotalCount 
    from C1 
) 
select C2.OverallPosition, 
     C2.OverallPositionTotalCount, 
     C2.AgePosition, 
     C2.AgeTotalCount, 
     C2.GenderPosition, 
     C2.GenderTotalCount 
from C2 
where C2.UserId = 4; 

SQL Fiddle

備選:

select C.OverallPosition, 
     C.OverallPositionTotalCount, 
     C.AgePosition, 
     C.AgeTotalCount, 
     C.GenderPosition, 
     C.GenderTotalCount 
from (
    select U.UserId, 
      S.TotalSale, 
      rank() over(order by S.TotalSale desc) as OverallPosition, 
      rank() over(partition by U.Age order by S.TotalSale desc) as AgePosition, 
      rank() over(partition by U.Gender order by S.TotalSale desc) as GenderPosition, 
      count(*) over() as OverallPositionTotalCount, 
      count(*) over(partition by U.Age) as AgeTotalCount, 
      count(*) over(partition by U.Gender) as GenderTotalCount 
    from dbo.[User] as U 
     cross apply (
        select max(S.TotalSale) as TotalSale 
        from dbo.Sales as S 
        where U.UserId = S.UserId 
        ) as S 
    ) as C 
where C.UserId = 4; 

SQL Fiddle

+0

你認爲哪一個表現更好? –

+0

我的猜測是第二個查詢。 @急於。 –

+0

感謝Mikael ...那裏有很好的代碼。 –

1

jsFiddle - 編輯:這是一個sqlFiddle,不是的jsfiddle :)

DECLARE @UserId INT = 4 

;with overall as 
(
    SELECT u.Name, u.UserId, RANK() OVER (ORDER BY max(s.TotalSale) DESC) OverallRank 
    FROM User u 
    JOIN Sales s on u.UserId = s.UserId 
    group by u.Name, u.UserId 
), 
age as (
    SELECT u.Name, u.UserId, RANK() OVER (ORDER BY max(s.TotalSale) DESC) AgeRank 
    FROM User u 
    JOIN Sales s on u.UserId = s.UserId 
    where u.age = (select age from @User where UserId = @UserId) 
    group by u.Name, u.UserId 
), 
gender as (
    SELECT u.Name, u.UserId, RANK() OVER (ORDER BY max(s.TotalSale) DESC) GenderRank 
    FROM User u 
    JOIN Sales s on u.UserId = s.UserId 
    where u.Gender = (select gender from @User where UserId = @UserId) 
    group by u.Name, u.UserId 
) 

SELECT o.OverallRank as OverallPosition, 
     (select count(*) from overall) as OverallTotalCount, 
     a.AgeRank as AgePosition, 
     (select count(*) from age) as AgeTotalCount, 
     g.GenderRank GenderPosition, 
     (select count(*) from gender) as GenderTotalCount 
FROM overall o 
JOIN age a on o.UserId = a.UserId 
JOIN gender g on o.UserId = g.UserId 
WHERE o.UserId = @UserId 
+0

謝謝湯姆,即使我結束了使用較短的版本,我感謝您的回覆。 –

1

這裏是完整的SQL Proc來做到這一點...基本上你必須手動做到這一點。 (注:我改變了表名,併爲TestUser對TestSales不碰撞內置的名字。)

CREATE PROCEDURE [dbo].[GetUserSales] 
    @paramUserId int 
AS 

BEGIN  

DECLARE @OverallPosition int 
DECLARE @OverallCount int 
DECLARE @AgePosition int 
DECLARE @AgeTotalCount int 
DECLARE @GenderPosition int 
DECLARE @GenderTotalCount int 

---------- 
-- OVERALL 
---------- 

SELECT @OverallCount = COUNT(UserId) FROM dbo.TestUser 

-- Add an extra 1 here for the user himself. 
SELECT @OverallPosition = COUNT(us.UserId) + 1 
FROM 
(
    SELECT tu.UserId, MAX(ts.TotalSale) as TopSale 
    FROM TestUser as tu 
    JOIN TestSales as ts ON tu.UserId = ts.UserId 
    GROUP BY (tu.UserId) 
) as us 
WHERE us.TopSale > (SELECT MAX(TotalSale) FROM TestSales WHERE UserId = @paramUserId) 


---------- 
-- AGE 
---------- 

SELECT @AgeTotalCount = COUNT(UserId) FROM TestUser WHERE Age = (SELECT Age FROM TestUser WHERE UserId = @paramUserId) 

-- Add an extra 1 here for hte user himself. 
SELECT @AgePosition = COUNT(usa.UserId) + 1 
FROM 
(
    SELECT tu.UserId, MAX(ts.TotalSale) as TopSale 
    FROM TestUser as tu 
    JOIN TestSales as ts ON tu.UserId = ts.UserId 
    WHERE tu.Age = (SELECT Age FROM TestUser WHERE UserId = @paramUserId) 
    GROUP BY (tu.UserId) 
) as usa 
WHERE usa.TopSale > (SELECT MAX(TotalSale) FROM TestSales WHERE UserId = @paramUserId) 


---------- 
-- GENDER 
---------- 

SELECT @GenderTotalCount = COUNT(UserId) FROM TestUser WHERE Gender = (SELECT Gender FROM TestUser WHERE UserId = @paramUserId) 

-- Add an extra 1 here for hte user himself. 
SELECT @GenderPosition = COUNT(usg.UserId) + 1 
FROM 
(
    SELECT tu.UserId, MAX(ts.TotalSale) as TopSale 
    FROM TestUser as tu 
    JOIN TestSales as ts ON tu.UserId = ts.UserId 
    WHERE tu.Gender = (SELECT Gender FROM TestUser WHERE UserId = @paramUserId) 
    GROUP BY (tu.UserId) 
) as usg 
WHERE usg.TopSale > (SELECT MAX(TotalSale) FROM TestSales WHERE UserId = @paramUserId) 


---------- 
-- RESULTSET 
---------- 
SELECT tu.UserId, tu.Name, 
     @OverallPosition as 'OverallPosition', @OverallCount as 'OverallCount', 
     @AgePosition as 'AgePosition', @AgeTotalCount as 'AgeTotalCount', 
     @GenderPosition as 'GenderPosition', @GenderTotalCount as 'GenderTotalCount' 
FROM TestUser as tu 
WHERE tu.UserId = @paramUserId 

END 
+0

喬,我結束了使用較短的版本,但仍然感謝您的答覆。再次感謝。 –

+0

感謝您的支持。另一個答案也是更好的表現。我在這裏也學到了一些東西 - 不知道「交叉應用」中的rank()順序(...)能力。好問題+1。 – Joe