2013-05-01 27 views
2

假设我有这样的数据:每组选择一行而不指定顺序?

create table People (ID int identity, Name nvarchar(50), Age int); 
insert into People values 
    ('Bill Jones', 50), 
    ('Bill Jones', 12), 
    ('Sam Smith', 23), 
    ('Jill Brown', 44), 
    ('Jill Brown', 67), 
    ('Jill Brown', 3) 

而这个查询:

select * from (
    select 
     ID, Name, Age, 
     row_number() over (partition by Name order by ID) [rownum] 
    from People 
) a where [rownum] = 1 

它成功地返回我每唯一名称一人。

ID NAME  AGE ROWNUM 
1 Bill Jones 50 1 
4 Jill Brown 44 1 
3 Sam Smith 23 1 

但是为了使用row_number(),我都必须指定一个order by,引起查询计划包括昂贵的排序操作。

query plan

我不关心返回其人;我只需要每个名字一个人。

有没有办法做到这一点没有排序?

你可以在这里看到我的查询和执行计划:http://sqlfiddle.com/#!3/3ee32/1/0

+0

尝试使用'order by 1/0',一个假的排序。它可能在QO上游戏足以放弃排序。 – RichardTheKiwi 2013-05-01 05:06:39

+3

排序并不总是很昂贵,并且不相信查询计划中的%s。你也可以尝试变种'选择身份证号码,姓名,年龄,身份证号码(从姓名分组中选择人名分钟)(ID)' – RichardTheKiwi 2013-05-01 05:10:02

+0

我认为我实际上是个白痴......无论如何,为了分割。更改为'1/0的顺序'确实会停止它通过ID *以及* Name排序。 – Blorgbeard 2013-05-01 05:17:09

回答

1

如何查询:

select 
    distinct Name, 
    (Select top 1 Age from People b where b.Name=a.Name) Age 
from People a 

OR

select b.* From(
    select min(ID) ID from people group by Name 
    ) a inner join People b on a.ID=b.ID 

与所有列。 事实:这些查询中没有一个用ROW_NUMBER()击败查询!

+0

有趣,但我实际上有几个列以外的年龄,所以我需要为每个子查询。 – Blorgbeard 2013-05-01 05:20:38

2

我知道不其optmised一个或没有,但它显示的记录,只要你想......没有ORDER BY子句/ .....

Select * from People a where id in (Select Top(1) id from people b where name in 
            (Select name from people group by name) and a.name=b.name) 

Sql Fidddle Demo Link

1

你可能会考虑此查询:

SELECT 
    a.* 
FROM 
    People a 
LEFT JOIN 
    People b 
ON 
    (a.Name = b.Name AND a.id > b.id) 
WHERE b.id IS NULL 

当我SQLFiddle运行它,它似乎有更好的表现:

原始查询:0.0146747

自联接:0.0071784

+1

你在哪里看到这些数字?我只能看到两个“1ms”。无论如何,我将它翻译成我的真实表格,而且它似乎更慢 - 两次表扫描,而不是一次扫描和排序。虽然有趣的技术! – Blorgbeard 2013-05-01 05:32:07

1

我真正看到不同的方式来写原始查询。你也可以考虑使用Common Table Expression。尽管我相信优化水平将几乎相同。但我更喜欢CTE

with cte 
as 
(
    select ID, Name, Age, 
      row_number() over (partition by Name order by ID) [rownum] 
    from People 
) 

select * 
from cte 
where [rownum] = 1