SQL, чтобы найти разницу между двумя строками - PullRequest
2 голосов
/ 26 февраля 2009

У меня есть база данных Informix, содержащая измеренные значения температуры для нескольких разных мест. Измерения проводятся каждые 15 минут для всех местоположений, а затем загружаются с отметкой времени в ту же таблицу. Таблица выглядит так:

locId    dtg               temp
aaa      2009-02-25 10:00  15
bbb      2009-02-25 10:00  20
ccc      2009-02-25 10:00  24
aaa      2009-02-25 09:45  13
ccc      2009-02-25 09:45  16
bbb      2009-02-25 09:45  18
ddd      2009-02-25 09:45  12
aaa      2009-02-25 09:30  11
ccc      2009-02-25 09:30  14
bbb      2009-02-25 09:30  15
ddd      2009-02-25 09:30  10

Теперь я хотел бы запрос, который представляет мне изменение температуры между двумя последними измерениями для всех станций. А также только те, которые имеют обновленное измерение. Например, в приведенной выше таблице местоположение ddd не будет включено. Таким образом, результат становится:

locId    change
aaa      2
bbb      2
ccc      8

Я много пробовал, но не могу найти хорошего решения. На самом деле это около 700 мест, которые запрашиваются с веб-страницы, поэтому я думаю, что запрос должен быть достаточно эффективным.

Был бы очень признателен за помощь!
// Jesper

Ответы [ 6 ]

4 голосов
/ 26 февраля 2009

Спасибо uglysmurf за предоставление данных в формате SQL.

При использовании IDS (IBM Informix Dynamic Server) версии 11.50 работает следующий запрос.

CREATE TEMP TABLE temps
(
    locId   CHAR(3),
    dtg     DATETIME YEAR TO MINUTE,
    temp    SMALLINT
);
INSERT INTO temps VALUES ('aaa', '2009-02-25 10:00', 15);
INSERT INTO temps VALUES ('bbb', '2009-02-25 10:00', 20);
INSERT INTO temps VALUES ('ccc', '2009-02-25 10:00', 24);
INSERT INTO temps VALUES ('aaa', '2009-02-25 09:45', 13);
INSERT INTO temps VALUES ('ccc', '2009-02-25 09:45', 16);
INSERT INTO temps VALUES ('bbb', '2009-02-25 09:45', 18);
INSERT INTO temps VALUES ('ddd', '2009-02-25 09:45', 12);
INSERT INTO temps VALUES ('aaa', '2009-02-25 09:30', 11);
INSERT INTO temps VALUES ('ccc', '2009-02-25 09:30', 14);
INSERT INTO temps VALUES ('bbb', '2009-02-25 09:30', 15);
INSERT INTO temps VALUES ('ddd', '2009-02-25 09:30', 10);

SELECT latest.locID, latest.temp, prior.temp,
       latest.temp - prior.temp as delta_temp,
       latest.dtg, prior.dtg
    FROM temps latest, temps prior
    WHERE latest.locId = prior.locId
      AND latest.dtg = prior.dtg + 15 UNITS MINUTE
      AND latest.dtg = (SELECT MAX(dtg) FROM temps);

Результаты (больше столбцов, чем запрошено, но вы можете легко обрезать список выбора):

aaa 15 13 2 2009-02-25 10:00 2009-02-25 09:45
ccc 24 16 8 2009-02-25 10:00 2009-02-25 09:45
bbb 20 18 2 2009-02-25 10:00 2009-02-25 09:45

Обратите внимание, что это решение не зависит от CURRENT (или NOW); он работает на последних записанных данных. Единственная часть инструкции SELECT, специфичная для IDS, это '+ 15 UNITS MINUTE'; это также может быть записано как '+ INTERVAL(15) MINUTE TO MINUTE' в Informix и как '+ INTERVAL '15' MINUTE' в стандартном SQL (если СУБД поддерживает типы INTERVAL). Использование DATETIME YEAR TO MINUTE в таблице зависит от Informix; в таком контексте полезно не хранить информацию, которая вас не интересует (например, секунды).

4 голосов
/ 26 февраля 2009
set now = select max(dtg) from table;
set then = select max(dtg) from table where dtg < now;

select locID, old.temp-new.temp from 
      table as old join
      table as new 
      on old.locId = new.locID
where
      old.dtg = then and
      new.dtg = now;

предполагает, что все времена будут точными

2 голосов
/ 26 февраля 2009

В псевдо-SQL вы можете выполнить запрос:

@now = Time Now

Select Oldest.LocId, Oldest.timestamp, Oldest.temp - Newest.temp as Change
(Select LocId, temp from Foo where timestamp < @now - 15 mins AND timestamp >= @now - 30 mins) Oldest
   left join
(Select LocId, temp from Foo where timestamp >= TimeNow - 15 mins) Newest
   on Oldest.LocId = Newest.LocId

Не уверен, если вы определите это как «хорошее» решение, но оно должно работать, если для каждого местоположения есть две точки данных.

1 голос
/ 26 февраля 2009

Я не верю, что Informix имеет аналитические функции, такие как Oracle, но если бы он это сделал, это было бы отличным местом для их использования. Далее следует пример Oracle, использующий аналитические функции lag и max.

Сценарий установки:

drop table temps;
create table temps (
locId varchar2(3),
dtg date,
temp number(3)
);

insert into temps values ('aaa', to_date('2009-02-25 10:00','yyyy-mm-dd hh:mi'), 15);
insert into temps values ('bbb', to_date('2009-02-25 10:00','yyyy-mm-dd hh:mi'), 20);
insert into temps values ('ccc', to_date('2009-02-25 10:00','yyyy-mm-dd hh:mi'), 24);
insert into temps values ('aaa', to_date('2009-02-25 09:45','yyyy-mm-dd hh:mi'), 13);
insert into temps values ('ccc', to_date('2009-02-25 09:45','yyyy-mm-dd hh:mi'), 16);
insert into temps values ('bbb', to_date('2009-02-25 09:45','yyyy-mm-dd hh:mi'), 18);
insert into temps values ('ddd', to_date('2009-02-25 09:45','yyyy-mm-dd hh:mi'), 12);
insert into temps values ('aaa', to_date('2009-02-25 09:30','yyyy-mm-dd hh:mi'), 11);
insert into temps values ('ccc', to_date('2009-02-25 09:30','yyyy-mm-dd hh:mi'), 14);
insert into temps values ('bbb', to_date('2009-02-25 09:30','yyyy-mm-dd hh:mi'), 15);
insert into temps values ('ddd', to_date('2009-02-25 09:30','yyyy-mm-dd hh:mi'), 10);
commit;

Oracle-специфический запрос с использованием аналитических функций:

select locId, change
  from (
select t.locId,
       t.dtg,
       t.temp,
       -- difference between this records temperature and the record before it 
       t.temp - lag(t.temp) over (partition by t.locId order by t.dtg) change,
       -- max date for this location
       max(t.dtg) over (partition by t.locId) maxDtg,
       max(t.dtg) over (partition by 1) overallMaxDtg
  from temps t
 order by t.locId, t.dtg
 ) where maxDtg = dtg -- only most recent measurement
     and overallMaxDtg = maxDtg -- only stations with an 'updated measurement'
     ;

Результат:

LOCID CHANGE

aaa   2
bbb   2
ccc   8

Хороший ресурс по аналитике Oracle: http://www.psoug.org/reference/analytic_functions.html

1 голос
/ 26 февраля 2009
declare @dt_latest datetime, @dt_prev datetime  

select @dt_latest = max(dtg) from Measures
select @dt_prev = max(dtg) from Measures where dtg < @dt_latest  

select Latest.Locid, Latest.temp - Prev.temp
from Measures as "Latest"
inner join Measures as "Prev" on Latest.Locid = Prev.Locid
where Latest.dtg = @dt_latest
and Prev.dtg = @dt_prev

Редактировать: так же, как в основном БКС, победить меня!

0 голосов
/ 26 февраля 2009

Попробуйте что-нибудь подобное. Возможно, он не очень эффективен, но, в отличие от некоторых других ответов, он возвращает diff для каждого LocID

SELECT DISTINCT LocID,
            (SELECT max(t3.temp)-min(t3.temp) from 
                   (SELECT TOP 2 T2.temp 
                    From Table2 T2 
                    Where (t2.Locid=t1.locid) 
                    order by DTG DESC) as t3
             ) as Diff
     FROM Table1 T1

Предостережение: я написал это с использованием tSQL, но постарался максимально придерживаться стандарта ANSI SQL для переносимости в Informix.

...