SQL to find the difference between two rows

12,611

Solution 1

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;

assumes that all times will be exact

Solution 2

Thanks to uglysmurf for providing the data in an SQL format.

Using IDS (IBM Informix Dynamic Server) version 11.50, the following query works.

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);

Results (more columns than requested, but you can easily trim the select list):

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

Note that this solution does not depend on CURRENT (or NOW); it works on the latest recorded data. The only part of the SELECT statement that is IDS-specific is the '+ 15 UNITS MINUTE'; that could also be written as '+ INTERVAL(15) MINUTE TO MINUTE' in Informix, and as '+ INTERVAL '15' MINUTE' in standard SQL (if the DBMS supports INTERVAL types). The use of DATETIME YEAR TO MINUTE in the table is Informix-specific; in a context like this, it is useful not to store information you are not interested in (such as the seconds).

Solution 3

In pseudo-SQL you could do the query:

@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

Not sure if you define this as a 'good' solution, but it should work providing there are two data points for each location.

Solution 4

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

Edit: same as BCS basically, beat me to it!

Solution 5

I don't believe Informix has analytic functions like Oracle, but if it did this would be a excellent place to use them. What follows is an Oracle example using the analytic functions lag and max.

Setup script:

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-specific query using analytic functions:

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'
     ;

Result:

LOCID CHANGE

aaa   2
bbb   2
ccc   8

Good resource on Oracle analytics: http://www.psoug.org/reference/analytic_functions.html

Share:
12,611
Admin
Author by

Admin

Updated on June 05, 2022

Comments

  • Admin
    Admin almost 2 years

    I have an Informix database containing measured temperature values for quite a few different locations. The measurements are taken every 15 min for all locations and then loaded with a timestamp into the same table. Table looks like this:

    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
    

    Now I would like a query that present me with the change in temperature between the two last measurements for all stations. And also, only the ones that has an updated measurement. For example in the table above, location ddd would not be included. So the result becomes:

    locId    change
    aaa      2
    bbb      2
    ccc      8
    

    I have tried alot but I can´t find any good solution. In reality it is about 700 locations that is asked from a web page so I think the query needs to be fairly efficient.

    Would really appreciate some help!
    //Jesper