Простой запрос для получения максимального значения для каждого идентификатора - PullRequest
18 голосов
/ 16 апреля 2009

ОК. У меня есть такая таблица:

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    Signal    Station    OwnerID
111   -120      Home        1
222   -95       Work        1

Я пытался использовать MAX (), и агрегация мешала, когда Station и OwnerID были разными для каждой записи. Мне нужно присоединиться?

Ответы [ 9 ]

18 голосов
/ 16 апреля 2009

Как то так? Соедините свою таблицу с самим собой и исключите строки, для которых был найден более высокий сигнал.

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
)

В этом списке будет указана одна строка для каждого наивысшего сигнала, поэтому для идентификатора может быть несколько строк.

15 голосов
/ 16 апреля 2009

Вы выполняете групповую максимальную / минимальную операцию. Это обычная ловушка: похоже, что-то, что должно быть легко сделать, но в SQL это не отягчает.

Существует несколько подходов (как стандартных ANSI, так и специфичных для поставщиков) к этой проблеме, большинство из которых являются неоптимальными во многих ситуациях. Некоторые выдадут вам несколько строк, если несколько строк имеют одинаковое максимальное / минимальное значение; некоторые не будут. Некоторые хорошо работают на столах с небольшим количеством групп; другие более эффективны для большего числа групп с меньшими строками на группу.

Вот обсуждение некоторых распространенных (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;
4 голосов
/ 16 апреля 2009

В классическом SQL-92 (без использования операций OLAP, используемых Quassnoi), вы можете использовать:

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 определяет максимальное значение сигнала для каждого идентификатора; объединение объединяет это с соответствующей строкой данных из основной таблицы.

Примечание: если есть несколько записей для определенного идентификатора, которые имеют одинаковую мощность сигнала, и эта сила равна MAX (), то вы получите несколько выходных строк для этого идентификатора.


Протестировано на 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 для этого теста - но, похоже, он дает правильный ответ. Это только показывает, что существует хотя бы одна СУБД, которая поддерживает обозначения. Тем не менее, я немного удивлен, что MS SQL Server нет - какую версию вы используете?


Меня не перестает удивлять, как часто вопросы SQL отправляются без имен таблиц.

2 голосов
/ 04 сентября 2016

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

1 голос
/ 28 октября 2015

Мы можем сделать, используя самостоятельное соединение

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;
1 голос
/ 16 апреля 2009
WITH q AS
         (
         SELECT  c.*, ROW_NUMBER() OVER (PARTITION BY id ORDER BY signal DESC) rn
         FROM    mytable
         )
SELECT   *
FROM     q
WHERE    rn = 1

Это вернет одну строку, даже если для данного ID.

есть дубликаты MAX(signal).

Наличие индекса на (id, signal) значительно улучшит этот запрос.

0 голосов
/ 30 мая 2019

выберите Я бы, max_signal, владелец, OwnerId ОТ ( выберите *, rank () over (разделение по порядку идентификатора по сигналу desc) как max_signal из таблицы ) где max_signal = 1;

0 голосов
/ 04 сентября 2016
SELECT * FROM StatusTable
WHERE Signal IN (
    SELECT A.maxSignal FROM
    (
        SELECT ID, MAX(Signal) AS maxSignal
        FROM StatusTable
        GROUP BY ID
    ) AS A
);
0 голосов
/ 16 апреля 2009
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 
...