2009-04-16 61 views
15

好,我有一個表是這樣的:簡單查詢抓住最大價值的每個ID

ID  Signal Station OwnerID 
111  -120  Home  1 
111  -130  Car  1 
111  -135  Work  2 
222  -98  Home  2 
222  -95  Work  1 
222  -103  Work  2 

這一切都是在同一天。我只需要查詢爲每個ID返回最大信號:

ID Signal Station OwnerID 
111 -120  Home  1 
222 -95  Work  1 

我試着使用MAX()和聚合與站和OWNERID對於每個記錄不同的食堂了。我需要做一個JOIN嗎?

+0

您正在使用哪個版本的SQL Server? – 2009-04-16 15:43:52

回答

15

這樣的事情?加入你自己的表格,排除找到更高信號的行。

select cur.id, cur.signal, cur.station, cur.ownerid 
from yourtable cur 
where not exists (
    select * 
    from yourtable high 
    where high.id = cur.id 
    and high.signal > cur.signal 
) 

這將爲每個最高信號列出一行,因此每個ID可能有多行。

+0

是的,如果信號對於多個電臺是相同的,那麼這會返回重複。 – 2009-04-16 12:46:20

+0

已編輯,因此您每個信號獲得多行,但沒有重複。如果您只想從信號最高的那些行中隨機選擇一行,請使用Quassnoi的答案。 – Andomar 2009-04-16 12:50:54

+0

是的,我認爲這是工作。我需要檢查數據。但是非常感謝。 – 2009-04-16 13:04:29

1
WITH q AS 
     (
     SELECT c.*, ROW_NUMBER() OVER (PARTITION BY id ORDER BY signal DESC) rn 
     FROM mytable 
     ) 
SELECT * 
FROM  q 
WHERE rn = 1 

這將返回一行,即使有一個給定IDMAX(signal)重複。

擁有(id, signal)的索引將大大改善此查詢。

+0

更好地使用聚合和jon方法比創建一個列。優化器可以作爲一個整體進行評估:這裏計算的列需要首先計算,所以這可能需要一個假脫機程序 – gbn 2009-04-16 13:29:44

+0

如果您有該列的索引(您應該),則聯接效率會降低。 – Quassnoi 2009-04-16 13:33:26

+0

+不適用於SQL Server 200,以防萬一 – gbn 2009-04-16 13:33:35

0
select a.id, b.signal, a.station, a.owner from 
mytable a 
join 
(SELECT ID, MAX(Signal) as Signal FROM mytable GROUP BY ID) b 
on a.id = b.id AND a.Signal = b.Signal 
3

在傳統的SQL-92(不使用Quassnoi所使用的OLAP操作),那麼你可以使用:

SELECT g.ID, g.MaxSignal, t.Station, t.OwnerID 
    FROM (SELECT id, MAX(Signal) AS MaxSignal 
      FROM t 
      GROUP BY id) AS g 
     JOIN t ON g.id = t.id AND g.MaxSignal = t.Signal; 

(未檢查的語法;假設你的表是 'T'。)

FROM子句中的子查詢標識每個id的最大信號值;該連接將它與主表中相應的數據行組合在一起。

注意:如果某個特定ID有幾個條目都具有相同的信號強度並且強度是MAX(),那麼您將爲該ID獲得多個輸出行。


測試對IBM Informix Dynamic Server的11.50.FC3在Solaris 10上運行:

+ CREATE TEMP TABLE signal_info 
(
    id  INTEGER NOT NULL, 
    signal INTEGER NOT NULL, 
    station CHAR(5) NOT NULL, 
    ownerid INTEGER NOT NULL 
); 
+ INSERT INTO signal_info VALUES(111, -120, 'Home', 1); 
+ INSERT INTO signal_info VALUES(111, -130, 'Car' , 1); 
+ INSERT INTO signal_info VALUES(111, -135, 'Work', 2); 
+ INSERT INTO signal_info VALUES(222, -98 , 'Home', 2); 
+ INSERT INTO signal_info VALUES(222, -95 , 'Work', 1); 
+ INSERT INTO signal_info VALUES(222, -103, 'Work', 2); 
+ SELECT g.ID, g.MaxSignal, t.Station, t.OwnerID 
    FROM (SELECT id, MAX(Signal) AS MaxSignal 
      FROM signal_info 
      GROUP BY id) AS g 
     JOIN signal_info AS t ON g.id = t.id AND g.MaxSignal = t.Signal; 

111  -120 Home 1 
222  -95  Work 1 

我命名爲這個測試表Signal_Info - 但它似乎產生正確的答案。 這隻顯示至少有一個支持該表示法的DBMS。但是,我有點驚訝,MS SQL Server沒有 - 您使用的是哪個版本?


它從來沒有停止多久SQL問題不表名稱被提交給我驚喜。

14

您正在按組進行最大/最小操作。這是一個常見的陷阱:它感覺應該很容易做,但是在SQL中,它更加惡化。

對於這個問題,有許多方法(包括標準ANSI和供應商特定的),其中大多數方法在許多情況下都不是最優的。當多行共享相同的最大/最小值時,有些會給你多行;有些不會。有些小組在桌子上工作得很好;其他人對於每個組中具有較小行數的較大數量的組更有效。

Here's a discussion一些常見的(MySQL偏差,但通常適用)。就個人而言,如果我知道沒有多個最大值(或者不關心如何得到它們),我經常傾向於null-left-self-join方法,我將以其他人尚未發佈的方式發佈:

SELECT reading.ID, reading.Signal, reading.Station, reading.OwnerID 
FROM readings AS reading 
LEFT JOIN readings AS highersignal 
    ON highersignal.ID=reading.ID AND highersignal.Signal>reading.Signal 
WHERE highersignal.ID IS NULL; 
0

我們可以採用自做加盟

SELECT T1.ID,T1.Signal,T2.Station,T2.OwnerID 
FROM (select ID,max(Signal) as Signal from mytable group by ID) T1 
LEFT JOIN mytable T2 
ON T1.ID=T2.ID and T1.Signal=T2.Signal; 

或者你也可以使用下面的查詢

SELECT t0.ID,t0.Signal,t0.Station,t0.OwnerID 
FROM mytable t0 
LEFT JOIN mytable t1 ON t0.ID=t1.ID AND t1.Signal>t0.Signal 
WHERE t1.ID IS NULL; 
2
 

with tab(id, sig, sta, oid) as 
(
select 111 as id, -120 as signal, 'Home' as station, 1 as ownerId union all 
select 111, -130, 'Car', 1 union all 
select 111, -135, 'Work', 2 union all 
select 222, -98, 'Home', 2 union all 
select 222, -95, 'Work', 1 union all 
select 222, -103, 'Work', 2 
) , 
tabG(id, maxS) as 
(
    select id, max(sig) as sig from tab group by id 
) 
select g.*, p.* from tabG g 
cross apply (select top(1) * from tab t where t.id=g.id order by t.sig desc) p 
 
0
 
SELECT * FROM StatusTable 
WHERE Signal IN (
    SELECT A.maxSignal FROM 
    (
     SELECT ID, MAX(Signal) AS maxSignal 
     FROM StatusTable 
     GROUP BY ID 
    ) AS A 
);