【问题标题】:Complicated grouping query, finding a ID not part of the GROUP BY复杂的分组查询,查找不属于 GROUP BY 的 ID
【发布时间】:2013-07-14 23:23:50
【问题描述】:

我在处理这个 SQL 查询时遇到了一些问题,首先是一些背景知识

表定义

create table [owner]
(
  [patientid] nvarchar(10) NOT NULL,
  [clientid] nvarchar(10) NOT NULL,
  [percentage] float NULL,
  [status] bit NOT NULL
)

alter table [owner] ADD CONSTRAINT PK_OWNER PRIMARY KEY CLUSTERED ([patientid],[clientid])

示例源数据

|患者 ID |客户 ID |百分比 |状态 | ---------------------------------------------- |宠物1 |所有者1 | 100 | 1 | |宠物2 |所有者2 | 75 | 1 | |宠物2 |所有者3 | 25 | 1 | |宠物3 |所有者4 | 10 | 1 | |宠物3 |所有者5 | 90 | 1 | |宠物3 |所有者6 | 100 | 0 | |宠物4 |所有者7 | 50 | 1 | |宠物4 |所有者8 | 50 | 1 |

我正在寻找的是,我希望拥有最高百分比的宠物拥有1 状态的主人,如果出现平局,它应该按主人姓名的字母顺序排列。

这是我想看到的输出

|患者 ID |客户 ID | ---------------------- |宠物1 |所有者1 | |宠物2 |所有者2 | |宠物3 |所有者5 | |宠物4 |所有者7 |

我得到的最接近的是

SELECT f1.[patientid]
      ,f1.[clientid]
  FROM [OWNER] f1
inner join 
(
  select [patientid], max([percentage]) as [percentage]
  from [owner]
  where status = 1
  group by [patientid]
) f2 on f1.[patientid] = f2.[patientid] and f1.[percentage] = f2.[percentage]
where status = 1

但是,这给了我两个 Pet4 的记录。

|患者 ID |客户 ID | ---------------------- |宠物1 |所有者1 | |宠物2 |所有者2 | |宠物3 |所有者5 | |宠物4 |所有者7 | |宠物4 |所有者8 |

处理这样的事情的正确方法是什么,所以我只得到一条记录,然后我在领带上应用字母顺序来找到一条记录?

这里是SQL Fiddle workspace,可以尝试任何答案。


编辑:

我想出了一个办法,但对我来说它有一股代码味道,有没有更“正确”的方法来做到这一点?

select distinct f3.[patientid], (
  SELECT top 1 f1.[clientid]
    FROM [OWNER] f1
  inner join 
  (
    select [patientid], max([percentage]) as [percentage]
    from [owner]
    where status = 1
    group by [patientid]
  ) f2 on f1.[patientid] = f2.[patientid] and f1.[percentage] = f2.[percentage]
  where status = 1 and f1.[patientid] = f3.[patientid]
  order by f1.[patientid], f1.[clientid]
)
from owner f3

【问题讨论】:

  • bluefeet 的解决方案更好:服务器将只读取一次行。对于上述解决方案,服务器将读取行两到三遍:f1f2f3

标签: sql sql-server sql-server-2005 group-by


【解决方案1】:

相关子查询也可以工作,但分区可能会更好地扩展

declare @tmpOwner table (
    PatientID varchar(50),
    ClientID varchar(50),
    Percentage int,
    Status smallint
)

insert @tmpOwner (PatientID,ClientID,Percentage,Status)
SELECT 'Pet1','Owner1',100,1 UNION
SELECT 'Pet2','Owner2',75,1 UNION
SELECT 'Pet2','Owner3',25,1 UNION
SELECT 'Pet3','Owner4',10,1 UNION
SELECT 'Pet3','Owner5',90,1 UNION
SELECT 'Pet3','Owner6',100,0 UNION
SELECT 'Pet4','Owner7',50,1 UNION
SELECT 'Pet4','Owner8',50,1


select x.PatientID,
(SELECT top 1 ClientID 
 FROM @tmpOwner 
 where Percentage=max(x.Percentage) 
 and x.PatientID=PatientID
 order by ClientID) Win_Owner
from @tmpOwner x
where x.Status=1
group by PatientID

【讨论】:

    【解决方案2】:

    我认为窗口函数,如@bluefeet 的回答,使这更容易阅读。但是您接近替代语法。 MIN() 可以解析绑定行的字母顺序。 (有些人认为 MIN() 只对数字起作用。)

    SELECT f1.[patientid]
         , MIN(f1.[clientid])
    FROM [OWNER] f1
    INNER JOIN
        (SELECT [patientid], max([percentage]) as [percentage]
         FROM [owner]
         WHERE status = 1
         GROUP BY [patientid]
        ) f2 
    ON  f1.[patientid] = f2.[patientid] 
    AND f1.[percentage] = f2.[percentage]
    GROUP BY f1.patientid
    

    【讨论】:

      【解决方案3】:

      您应该能够使用row_number() 来通过patientid 应用分区并按百分比和clientid 对其进行排序来获得结果:

      select patientid, clientid
      from
      (
        select patientid, clientid, percentage, status,
          row_number() over(partition by patientid 
                            order by percentage desc, clientid) rn
        from owner
        where status = 1
      ) d
      where rn = 1;
      

      SQL Fiddle with Demo

      【讨论】:

      • 啊,比我的解决方案好多了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-20
      • 1970-01-01
      • 2022-01-03
      • 1970-01-01
      • 1970-01-01
      • 2010-11-30
      • 1970-01-01
      相关资源
      最近更新 更多