2013-05-01 26 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