2009-04-16 6 views
15

OK J'ai une table comme ceci:requête simple à Grab Max Value pour chaque 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 

C'est tout pour le même jour. J'ai juste besoin de la requête pour renvoyer le signal max pour chaque ID:

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

J'ai essayé d'utiliser MAX() et l'agrégation Messes avec la station et OwnerID étant différente pour chaque enregistrement. Ai-je besoin de faire un JOIN?

+0

Quelle version de SQL Server utilisez-vous? –

Répondre

15

Quelque chose comme ça? Joignez-vous à votre table avec elle-même et excluez les lignes pour lesquelles un signal plus élevé a été trouvé.

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 
) 

Ceci énumérerait une ligne pour chaque signal le plus haut, donc il pourrait y avoir plusieurs lignes par identifiant.

+0

Oui, cela renvoie des doublons si le signal est le même pour plusieurs stations. –

+0

Modifié afin d'obtenir plusieurs lignes par signal, mais pas de doublons. Utilisez la réponse de Quassnoi si vous voulez seulement une rangée aléatoire parmi celles qui ont le signal le plus élevé. – Andomar

+0

Oui, je pense que cela fonctionne. J'ai besoin de vérifier les données. Mais merci beaucoup. –

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 

Cela renverra une ligne, même s'il y a des doublons de MAX(signal) pour un ID donné.

Avoir un index sur (id, signal) va grandement améliorer cette requête.

+0

Mieux vaut utiliser la méthode aggregate et jon que de créer une colonne. L'Optimiseur peut évaluer est dans son ensemble: la colonne calculée ici a besoin calculé d'abord pour cela plus que probablement besoin d'une bobine quelque part – gbn

+0

Si vous avez un index sur cette colonne (que vous devriez), la jonction sera moins efficace. – Quassnoi

+0

+ pas pour SQL Server 200 juste au cas – gbn

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 
+0

je reçois une « erreur de syntaxe dans la clause » erreur –

+0

@thegreekness: avez-vous besoin d'inclure un explicite entre les alias de table? mytable en tant que JOIN (SELECT ...) AS b? Vous ne devriez pas, mais ... –

+0

Je viens de réaliser - la condition ON doit aussi spécifier un signal de jointure. –

3

Dans classique SQL-92 (ne pas utiliser les opérations OLAP utilisées par Quassnoi), vous pouvez utiliser:

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; 

(syntaxe non cochée, suppose que votre table est 't'.)

La sous-requête de la clause FROM identifie la valeur de signal maximale pour chaque identifiant; la jointure combine cela avec la ligne de données correspondante de la table principale. NB: s'il y a plusieurs entrées pour un ID spécifique qui ont toutes la même intensité de signal et que la force est MAX(), alors vous obtiendrez plusieurs lignes de sortie pour cet ID.


contre IBM Testée Informix Dynamic Server 11.50.FC3 en cours d'exécution sur 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 

Je l'ai appelé la table Signal_Info pour ce test - mais il semble produire la bonne réponse. Cela montre seulement qu'il y a au moins un SGBD qui supporte la notation. Cependant, je suis un peu surpris que MS SQL Server ne - quelle version utilisez-vous?


Il ne cesse de me surprendre comment sont soumis souvent des questions SQL sans noms de table.

+0

je reçois une « erreur de syntaxe dans la clause FROM » erreur et il pointe vers la jointure –

14

Vous effectuez une opération maximale/minimale par groupe. C'est un piège courant: on dirait que quelque chose devrait être facile à faire, mais en SQL, ce n'est pas grave.

Il existe un certain nombre d'approches (ANSI standard et spécifiques au fournisseur) à ce problème, dont la plupart sont sous-optimales dans de nombreuses situations. Certains vous donneront plusieurs lignes lorsque plus d'une ligne partage la même valeur maximale/minimale; certains ne le feront pas. Certains fonctionnent bien sur des tables avec un petit nombre de groupes; d'autres sont plus efficaces pour un plus grand nombre de groupes avec des rangées plus petites par groupe.

Here's a discussion de certains des plus courants (MySQL-biased mais généralement applicable). Personnellement, si je sais qu'il n'y a pas de maxima multiples (ou que je m'en fous) je tends souvent vers la méthode null-left-self-join, que je posterai comme personne d'autre n'a encore:

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

L'utilisation de « lecture » et alias « highersignal » fait la compréhension de la requête un jeu d'enfant! Je vous remercie. – Sabuncu

0

Nous pouvons le faire en utilisant l'auto rejoindre

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; 

Ou vous pouvez également utiliser la requête suivante

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

Ajouter des explications – HaveNoDisplayName

0
 
SELECT * FROM StatusTable 
WHERE Signal IN (
    SELECT A.maxSignal FROM 
    (
     SELECT ID, MAX(Signal) AS maxSignal 
     FROM StatusTable 
     GROUP BY ID 
    ) AS A 
); 
Questions connexes