2015-11-12 247 views
0

這裏是SQL益智挑戰你:SQL拼圖 - UNION ALL沒有UNION ALL

編寫一個查詢,將在三個不同的類別選擇前5名的記錄。

事情是這樣的:

select top 5 name, age from table1 where category = 22 order by age desc 
union all 
select top 5 name, age from table1 where category = 27 order by age desc 
union all 
select top 5 name, age from table1 where category = 53 order by age desc 

但做到這一點不使用UNION或UNION ALL

如果您使用的是一些特定供應商的SQL擴展請註明您正在使用的數據庫

+0

和其他方式將三個子表加入到一個15條記錄'骨架' –

+1

TOP 5是供應商特定的。 – Parfait

+0

嗯......你說得對。 TOP 5是供應商特定的!哈哈!什麼停留在一個單一的數據庫一段時間可以做... – Anton

回答

3

經典top-n-per-group,是不是它?

使用SQL Server語法。 ROW_NUMBER()應該是人人享有體面的數據庫在2015年

WITH 
CTE 
AS 
(
    select 
     name 
     ,age 
     ,ROW_NUMBER() OVER (PARTITION BY category ORDER BY age desc) AS rn 
    from table1 
    where category IN (22, 27, 53) 
) 
SELECT 
    name 
    ,age 
FROM CTE 
WHERE rn <= 5 
; 

UNION在某種意義上是一樣OR

如果你的表中有一個主鍵ID你可以重寫本查詢:

SELECT name, age 
FROM table1 
WHERE 
    ID IN (select top 5 ID from table1 where category = 22 order by age desc) 
    OR 
    ID IN (select top 5 ID from table1 where category = 27 order by age desc) 
    OR 
    ID IN (select top 5 ID from table1 where category = 53 order by age desc) 

通常情況下,UNION ALL會比這更有效,雖然。

+0

這裏的CTE只是爲了更容易編寫,但'ROW_NUMBER'是關鍵部分。 intersystems-cache是​​否有類似的東西? –

+0

OP確實說過「如果您使用某些特定於供應商的SQL擴展,請指定您正在使用的數據庫」 –

+0

否,緩存中沒有ROW_NUMBER' – Anton

2

如果您使用某些特定於供應商的SQL擴展名,請指定您正在使用的數據庫爲 。

SQL服務器,您可以使用排序功能ROW_NUMBER()PARTITION BY category生成每個組類別的等級數,以及你正在尋找三類進行過濾。然後,您可以過濾只得到了行已居數小於5,這將讓你頂最古老的5名每個類別:

SELECT name, age 
FROM 
(
    SELECT 
     name, age, 
     ROW_NUMBER() OVER(PARTITION BY category ORDER BY age DESC)AS RN 
    FRO< table1 
    WHERE category IN(22,27, 53) 
) AS t 
WHERE RN <= 5; 
+0

ROW_NUMBER是供應商特定的還是ANSI SQL? – Anton

+0

@Anton - 您正在與哪些RDBMS合作?你沒有指定RDBMS –

+0

任何支持分析函數的數據庫都應該有row_number,這幾乎是任何現代主流RDBMS – Hambone

1

排名可以通過自聯接進行昂貴的模擬。這是僞SQL(即可能需要一點點語法調整)的子選擇做自我加入的解決方案。這是你的意思嗎?

select name, age from ( 
    select a.name,a.age, count(*) as rank from 
    table1 a join table1 b on a.category=b.category 
    where a.category in (22,27,53) and 
     ((b.age+hash(b.name,b.age) >=a.age+hash(a.name,a.age)) 
) c 
group by c.name, c.age 
where rank <= 5 
3

如果你真的想要前5名,那麼你可能需要一個決勝局。 name是獨特的每age

select t.name, t.age from T t 
where t.category in (22, 27, 53) and 5 >= (
    select count(*) from T t2 
    where t2.category = t.category 
     and (t2.age >= t.age or t2.age = t.age and t2.name >= t.name) 
) 
+0

好的一個,它的伎倆。雖然你的代碼是按ASC年齡排序的。我必須在最後一個條款中替換<= with =>以將其變爲時間DESC。 – Anton

+0

我改正了。不知道爲什麼我忽略了這一點。 – shawnt00