Requête simple à Grab Max Value pour chaque ID
-
09-09-2019 - |
Question
OK J'ai une table comme ceci:
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
Ceci 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'essayé d'utiliser MAX () et l'agrégation Messes avec la station et OwnerID étant différente pour chaque enregistrement. Est-ce que je dois faire JOIN?
La solution
Quelque chose comme ça? Joignez-vous à votre table avec elle-même, et d'exclure 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
)
Cette liste serait une ligne pour chaque signal le plus élevé, donc il peut y avoir plusieurs lignes par id.
Autres conseils
Vous faites une opération maximum / minimum sage groupe. Ceci est un piège commun. Il se sent comme quelque chose qui devrait être facile à faire, mais dans SQL il est aggravatingly pas
Il y a un certain nombre d'approches (à la fois la norme ANSI et spécifique au fournisseur) à ce problème, dont la plupart sont sous-optimaux dans de nombreuses situations. Certains vous donneront plusieurs lignes lorsque plus d'une ligne partage la même valeur maximum / minimum; d'autres non. Certains fonctionnent bien sur les tables avec un petit nombre de groupes; d'autres sont plus efficaces pour un plus grand nombre de groupes avec de plus petites lignes par groupe.
Voici une discussion de quelques-unes des plus courantes (MySQL biaisées mais généralement applicables). Personnellement, si je sais il n'y a pas plusieurs maxima (ou ne se soucient pas de les obtenir) Je tends souvent vers la méthode null-gauche-autojointure, 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;
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')
Le sous-requête dans la clause FROM identifie la valeur maximale du signal pour chaque ID; la jointure se combine avec celle de 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 tous la même intensité du signal et que la force est le MAX (), vous obtiendrez plusieurs lignes de sortie pour cet ID
Testé contre IBM 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 ya au moins un SGBD qui prend en charge la notation. Cependant, je suis un peu surpris de constater que MS SQL Server ne - quelle version vous utilisez
Il ne cesse de me surprendre comment sont soumis souvent des questions SQL sans noms de table.
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
WITH q AS
(
SELECT c.*, ROW_NUMBER() OVER (PARTITION BY id ORDER BY signal DESC) rn
FROM mytable
)
SELECT *
FROM q
WHERE rn = 1
retourne une ligne, même s'il y a des doublons de MAX(signal)
pour un ID
donné.
Avoir un index sur (id, signal)
améliorera grandement cette requête.
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;
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;
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
SELECT * FROM StatusTable WHERE Signal IN ( SELECT A.maxSignal FROM ( SELECT ID, MAX(Signal) AS maxSignal FROM StatusTable GROUP BY ID ) AS A );
sélectionnez id, max_signal, propriétaire, OWNERID DE ( sélectionner *, le rang () plus (partition par ordre d'id par le signal desc) comme max_signal de la table ) où max_signal = 1;