TSQL根据不同的字段计算各种%

时间:2014-02-17 18:06:06

标签: sql sql-server tsql

我正在2012 MS-SQL服务器上运行,并且在其他字段中有一个包含Age,Gender的表USER,以及一个包含销售记录的SALES表。

我目前正在计算销售排行榜,显示按其销售人员订购的销售人员列表,以便举例说明此列表根据其最高销售额返回各种销售代表。在列表中间的某个地方我们有托马斯先生,让我们说#4。

我目前的任务是展示Thomas与具有相同年龄的销售代表的比较,以及他与具有与他相同性别的销售代表的比较。计算将返回与上述整体列表不同的结果。

我理想的存储过程将收到1个参数(UserId)并返回以下单个记录 值:OverallPosition,OverallPositionTotalCount,AgePosition,AgeTotalCount,GenderPosition,GenderTotalCount

DATA SAMPLE:

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  

理想的计算结果
由于托马斯(userId 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的值(存储过程将接收UserId作为参数)而不是实际列表。

预期退货
OverallPosition = 4,
OverallPositionTotalCount = 6,
AgePosition = 2,
AgeTotalCount = 4,
GenderPosition = 3,
GenderTotalCount = 4

正如我在评论中所述,我真的不知道如何解决这个问题。我希望有人愿意帮助!!

3 个答案:

答案 0 :(得分: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

答案 1 :(得分: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

答案 2 :(得分: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