按ID分组前3个结果

时间:2012-05-14 17:56:18

标签: sql sql-server sql-server-2008

我有一张桌子:

+---------+--------------------+--------------------------+
| imd_id  | Total TRx per Plan |        plan name         |
+---------+--------------------+--------------------------+
| 1111005 | 397.1556           | Medicaid Illinois (Idpa) |
| 1111005 | 25.7691            | Self Pay                 |
| 1111005 | 24.4355            | Tricare North Region     |
| 1111005 | 15.0312            | 0                        |
| 1111005 | 8.8425             | 0                        |
| 1111005 | 8.3139             | 0                        |
| 1111005 | 7.0534             | 0                        |
| 1111005 | 6.2588             | 0                        |
| 1111005 | 6.0358             | Bravo Health             |
| 1111005 | 5.9872             | 0                        |
| 1111531 | 133.9664           | Medicaid Al              |
| 1111531 | 29.2318            | 0                        |
| 1111531 | 23.2499            | 0                        |
| 1111531 | 21.9774            | 0                        |
| 1111531 | 14.9269            | 0                        |
| 1111531 | 10.1903            | Self Pay                 |
| 1111531 | 5.4962             | 0                        |
| 1111531 | 5.3409             | Bcbs Federal             |
| 1111531 | 4.4801             | 0                        |
| 1111531 | 3.8003             | 0                        |
+---------+--------------------+--------------------------+

并尝试生成看起来像这样的数据

+---------+--------------------------+----------+---------------+-----------+----------------------+----------+
| imd_id  |      TopFirstPlan       | TopFirst | TopSecondPlan | TopSecond |    TopThirdPlan     | TopThird |
+---------+--------------------------+----------+---------------+-----------+----------------------+----------+
| 1111005 | Medicaid Illinois (Idpa) | 0.78     | Self Pay      | 0.05      | Tricare North Region | 0.04     |
| 1111531 | MEDICAID ALABAMA (AL)    | 0.5      | Self Pay      | 0.04      | Bcbs Federal         | 0.02     |
+---------+--------------------------+----------+---------------+-----------+----------------------+----------+

请注意,创建TOPFIRST,TOP SECOND,TOP THIRD的方式是相应的Total TRx per Plan除以特定IMD_ID的计划总和。

到目前为止,我有这个:

 select distinct a.imd_id,'topone'=
    (select top 1 totalrxperplan 
        from book1 b
      where b.imd_id =  a.imd_id)/
      (select SUM(totalrxperplan) 
        from book1 b
        where b.imd_id = a.imd_id)
      ,'topplan2'=
      (select top 1 xifinplanname 
        from book1 b
      where b.imd_id =  a.imd_id)
from book1 a
order by 1 asc

此查询将返回:

+---------+--------------------------+----------+
| imd_id  |      TopFirstPlan1       | TopFirst |
+---------+--------------------------+----------+
| 1111005 | Medicaid Illinois (Idpa) | 79%      |
| 1111531 | MEDICAID ALABAMA (AL)    | 53%      |
+---------+--------------------------+----------+

但我需要添加其他列。

请注意,我们会ignore the plan name where it is 0

5 个答案:

答案 0 :(得分:3)

CREATE TABLE #x(imd_id INT, totalrxperplan FLOAT, xifinplanname NVARCHAR(64));

INSERT #x VALUES
(1111005,397.1556 ,'Medicaid Illinois (Idpa)'),
(1111005,25.7691  ,'Self Pay                '),
(1111005,24.4355  ,'Tricare North Region    '),
(1111005,15.0312  ,'0                       '),
(1111005,8.8425   ,'0                       '),
(1111005,8.3139   ,'0                       '),
(1111005,7.0534   ,'0                       '),
(1111005,6.2588   ,'0                       '),
(1111005,6.0358   ,'Bravo Health            '),
(1111005,5.9872   ,'0                       '),
(1111531,133.9664 ,'Medicaid Al             '),
(1111531,29.2318  ,'0                       '),
(1111531,23.2499  ,'0                       '),
(1111531,21.9774  ,'0                       '),
(1111531,14.9269  ,'0                       '),
(1111531,10.1903  ,'Self Pay                '),
(1111531,5.4962   ,'0                       '),
(1111531,5.3409   ,'Bcbs Federal            '),
(1111531,4.4801   ,'0                       '),
(1111531,3.8003   ,'0                       ');

现在查询:

WITH cte1(id,pn,s) AS 
(
    SELECT imd_id, xifinplanname, 
      ROUND(totalrxperplan/SUM(totalrxperplan) OVER (PARTITION BY imd_id),2,1)
    FROM #x WHERE xifinplanname <> '0'
),
cte2(id,pn,s,rn) AS 
(
    SELECT id,pn,s,ROW_NUMBER() OVER (PARTITION BY id ORDER BY s DESC)
    FROM cte1
)
SELECT 
  id, 
  TopFirstPlan  = MAX(CASE WHEN rn = 1 THEN pn END),
  TopFirst      = MAX(CASE WHEN rn = 1 THEN s  END),
  TopSecondPlan = MAX(CASE WHEN rn = 2 THEN pn END),
  TopSecond     = MAX(CASE WHEN rn = 2 THEN s  END),
  TopThirdPlan  = MAX(CASE WHEN rn = 3 THEN pn END),
  TopThird      = MAX(CASE WHEN rn = 3 THEN s  END)
FROM cte2 
WHERE rn <= 3
GROUP BY id;

如果我的假设是正确的,输出数字与你的不匹配,因为你想要包含偶数&#39; 0&#39;计划在总计中,而不是在结果中,那么你可以只改变where子句:

WITH cte1(id,pn,s) AS 
(
    SELECT imd_id, xifinplanname, 
      ROUND(totalrxperplan/SUM(totalrxperplan) OVER (PARTITION BY imd_id), 2, 1)
    FROM #x -- < -- removed where clause from here
),
cte2(id,pn,s,rn) AS 
(
    SELECT id,pn,CONVERT(DECIMAL(3,2), s),ROW_NUMBER() OVER 
      (PARTITION BY id ORDER BY s DESC)
    FROM cte1 WHERE pn <> '0' -- <-- moved where clause here
)
SELECT 
  id, 
  TopFirstPlan  = MAX(CASE WHEN rn = 1 THEN pn END),
  TopFirst      = MAX(CASE WHEN rn = 1 THEN s  END),
  TopSecondPlan = MAX(CASE WHEN rn = 2 THEN pn END),
  TopSecond     = MAX(CASE WHEN rn = 2 THEN s  END),
  TopThirdPlan  = MAX(CASE WHEN rn = 3 THEN pn END),
  TopThird      = MAX(CASE WHEN rn = 3 THEN s  END)
FROM cte2 
WHERE rn <= 3
GROUP BY id;

答案 1 :(得分:1)

如果我理解正确,关键是在每个imd_id中按总计计算组。您可以使用windows函数row_number()。

执行此操作

最终查询类似于:

select imd_id,
       max(case when therank = 1 then plan_name end) as firstplan,
       max(case when therank = 1 then tot/imd_tot end) as firstplan_ratio,
       ...
from (select t.*, row_number() over (partition by imd_id order by tot desc) as therank,
             sum(tot) over (partition by imd_id) as imd_tot
      from (select imd_id  plan_name, sum(total_trx_per_plan) as tot
            from t
            group by imd_id  plan_name
           ) t
     ) t
group by imd_id

答案 2 :(得分:1)

我不确定Aaron的总数,而不是per-id总数,是OP想要的,所以这是我的建议,基本上是Aaron建议但具有不同的小数值:

更新以更正错误副本,并解释为“0”规则。

with R1 as (
  select
    imd_id,
    [plan name],
    1e0*[Total TRx per Plan]/sum([Total TRx per Plan]) over (partition by imd_id) as pct,
    row_number() over (
      partition by imd_id
      order by case when [plan name] = '0' then -1 else [Total TRx per Plan] end desc
    ) as rn
  from @Test
) 
  select
    imd_id,
    max(case when rn=1 then [plan name] end) as TopFirstPlan,
    max(case when rn=1 then pct end) as TopFirst,
    max(case when rn=2 then [plan name] end) as TopSecondPlan,
    max(case when rn=2 then pct end) as TopSecond,
    max(case when rn=3 then [plan name] end) as TopThirdPlan,
    max(case when rn=3 then pct end) as TopThird
  from R1
  where rn <= 3
  group by imd_id
  order by imd_id

答案 3 :(得分:1)

DECLARE @Test TABLE
(
    imd_id                  INT NOT NULL,
    [Total TRx per Plan]    NUMERIC(9,4) NOT NULL,
    [plan name]             NVARCHAR(50) NOT NULL
);

INSERT  @Test (imd_id, [Total TRx per Plan], [plan name])
VALUES  (1111005,397.1556,'Medicaid Illinois (Idpa) ')
,(1111005,25.7691 ,'SelfPay')
,(1111005,24.4355 ,'TricareNorthRegion')
,(1111005,15.0312 ,'0')
,(1111005,8.8425  ,'0')
,(1111005,8.3139  ,'0')
,(1111005,7.0534  ,'0')
,(1111005,6.2588  ,'0')
,(1111005,6.0358  ,'BravoHealth')
,(1111005,5.9872  ,'0')
,(1111531,133.9664,'MedicaidAl')
,(1111531,29.2318 ,'0')
,(1111531,23.2499 ,'0')
,(1111531,21.9774 ,'0')
,(1111531,14.9269 ,'0')
,(1111531,10.1903 ,'SelfPay')
,(1111531,5.4962  ,'0')
,(1111531,5.3409  ,'BcbsFederal')
,(1111531,4.4801  ,'0')
,(1111531,3.8003  ,'0');



SELECT  c.imd_id,
        MAX(CASE WHEN c.RowNum = 1 THEN c.[plan name] END) AS TopFirstPlan,
        MAX(CASE WHEN c.RowNum = 1 THEN c.TruncatedValue END) AS TopFirst,
        MAX(CASE WHEN c.RowNum = 2 THEN c.[plan name] END) AS TopSecondPlan,
        MAX(CASE WHEN c.RowNum = 2 THEN c.TruncatedValue END) AS TopSecond,
        MAX(CASE WHEN c.RowNum = 3 THEN c.[plan name] END) AS TopThirdPlan,
        MAX(CASE WHEN c.RowNum = 3 THEN c.TruncatedValue END) AS TopThird
FROM
(
        SELECT  b.imd_id,
                b.[plan name],
                b.TruncatedValue,
                ROW_NUMBER() OVER(PARTITION BY b.imd_id ORDER BY b.TruncatedValue DESC) AS RowNum
        FROM
        (
                SELECT  a.imd_id,
                        a.[plan name],
                        ROUND(a.[Total TRx per Plan] / SUM(a.[Total TRx per Plan]) OVER(PARTITION BY a.imd_id), 2, 1) AS TruncatedValue
                FROM    @Test a
        ) b
        WHERE   b.[plan name] <> '0'
) c
WHERE   c.RowNum < 4
GROUP BY c.imd_id

结果:

imd_id  TopFirstPlan             TopFirst                        TopSecondPlan TopSecond                       TopThirdPlan       TopThird
------- ------------------------ ------------------------------- ------------- ------------------------------- ------------------ -------------------------------
1111005 Medicaid Illinois (Idpa) 0.78000000000000000000000000000 SelfPay       0.05000000000000000000000000000 TricareNorthRegion 0.04000000000000000000000000000
1111531 MedicaidAl               0.53000000000000000000000000000 SelfPay       0.04000000000000000000000000000 BcbsFederal        0.02000000000000000000000000000

答案 4 :(得分:1)

我提出了这样的疑问:

WITH totals AS (
  SELECT imd_id, sum(t_trx_per_plan) AS ttl
    FROM plans
   GROUP BY imd_id),
ranks(imd_id,t_trx_per_plan,plan_name,prc,rank) AS (
  SELECT p.imd_id,p.t_trx_per_plan,p.plan_name,
    CAST(p.t_trx_per_plan / t.ttl * 100 AS NUMERIC(5,2)) AS prc,
    rank() OVER (PARTITION BY p.imd_id
        ORDER BY (p.t_trx_per_plan / t.ttl * 100) DESC) AS rank
    FROM plans p
    JOIN totals t ON t.imd_id = p.imd_id
   WHERE p.plan_name != '0')
SELECT
    ttl.imd_id,
    f.plan_name AS "FirstPlan",f.prc AS "First",
    s.plan_name AS "SecondPlan",s.prc AS "Second",
    t.plan_name AS "ThirdPlan",t.prc AS "Third"
  FROM totals ttl
  LEFT JOIN ranks f ON f.imd_id = ttl.imd_id AND f.rank = 1
  LEFT JOIN ranks s ON s.imd_id = ttl.imd_id AND s.rank = 2
  LEFT JOIN ranks t ON t.imd_id = ttl.imd_id AND t.rank = 3;

第一个CTE totals正在列出imd_id以及每个计划的总交易量。第二次CTE ranks是排名计划。请注意,我使用了rank()函数,如果计划每个计划的事务数量相同,则会产生相同的排名。如果要正确处理此类情况,则需要使用row_number()窗口函数并在ORDER BY子句中添加额外的OVER列,唯一的可能是使用:

row_number() OVER (PARTITION BY p.imd_id
    ORDER BY (p.t_trx_per_plan / t.ttl * 100) DESC, p.plan_name) AS rank

您可以检查输出here