Reputation: 63
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
Upvotes: 2
Views: 13739
Reputation: 4087
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.
Upvotes: 2
Reputation: 753665
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).
Upvotes: 4
Reputation: 11688
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
Upvotes: 1
Reputation: 78516
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
Upvotes: 4
Reputation: 34909
Give something like this a try. It may not be super efficient, but unlike some of the other answers it will return the dif for each 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
Caveat: I wrote this using tSQL, but tried to stick to standard ANSI SQL as much as possible for portability to Informix.
Upvotes: 0
Reputation: 5922
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!
Upvotes: 1