English 中文(简体)
Finding runs of a particular value
原标题:

I have a table in Oracle 10 that is defined like this:

LOCATION   HOUR              STATUS
--------------------------------------
10         12/10/09 5:00PM      1
10         12/10/09 6:00PM      1
10         12/10/09 7:00PM      2
10         12/10/09 8:00PM      1
10         12/10/09 9:00PM      3
10         12/10/09 10:00PM     3
10         12/10/09 11:00PM     3

This table continues for various locations and for a small number of status values. Each row covers one hour for one location. Data is collected from a particular location over the course of that hour, and processed in chunks. Sometimes the data is available, sometimes it isn t, and that information is encoded in the status. I am trying to find runs of a particular status, so that I could convert the above table into something like:

LOCATION   STATUS     START               END
-----------------------------------------------------------
10         1          12/10/09 5:00PM     12/10/09 7:00PM
10         2          12/10/09 7:00PM     12/10/09 8:00PM  
10         1          12/10/09 8:00PM     12/10/09 9:00PM
10         3          12/10/09 9:00PM     12/11/09 12:00AM 

Basically condensing the table into rows that define each stretch of a particular status. I have tried various tricks, like using lead/lag to figure out where starts and ends are and such, but all of them have met with failure. The only trick that works so far is going one by one through the values programatically, which is slow. Any ideas for doing it directly in Oracle? Thanks!

最佳回答

Here s an ANSI SQL solution:

select      t1.location
,           t1.status
,           min(t1.hour)                                      AS "start" -- first of stretch of same status
,           coalesce(t2.hour, max(t1.hour) + INTERVAL  1  HOUR) AS "end"
from        t_intervals t1             -- base table, this is what we are condensing
left join   t_intervals t2             -- finding the first datetime after a stretch of t1
on          t1.location = t2.location  -- demand same location
and         t1.hour     < t2.hour      -- demand t1 before t2
and         t1.status  != t2.status    -- demand different status 
left join   t_intervals t3             -- finding rows not like t1, with hour between t1 and t2
on          t1.location = t3.location
and         t1.status  != t3.status
and         t1.hour     < t3.hour
and         t2.hour     > t3.hour
where       t3.status is null          -- demand that t3 does not exist, in other words, t2 marks a status transition
group by    t1.location                -- condense on location, status.
,           t1.status
,           t2.hour                    -- this pins the status transition
order by    t1.location
,           t1.status
,           min(t1.hour)
问题回答

OK, I apologize for not knowing Oracle syntax, but I hope that the below Sybase one is clear enough (I split it into 3 queries creating 2 temp tables for readbility but you can just re-unit as sub-queries. I don t know how to add/subtract 1 hour in Oracle, dateadd(hh...) does it in Sybase

SELECT * FROM T
INTO   #START_OF_PERIODS
WHERE NOT EXISTS (
    SELECT 1 FROM T_BEFORE
    WHERE T.LOCATION = T_BEFORE.LOCATION
      AND T.STATUS   = T_BEFORE.STATUS
      AND T.HOUR     = dateadd(hh, T_BEFORE.HOUR, 1)
   )

SELECT * FROM T
INTO   #END_OF_PERIODS
WHERE NOT EXISTS (
    SELECT 1 FROM T_AFTER
    WHERE T.LOCATION = T_AFTER.LOCATION
      AND T.STATUS   = T_AFTER.STATUS
      AND T.HOUR     = dateadd(hh, T_AFTER.HOUR, -1)
   )

SELECT T1.LOCATION, T1.STATUS, T1.HOUR AS  START , MIN(T2.HOUR) AS  END 
FROM   #START_OF_PERIODS  T1 , #END_OF_PERIODS  T2 
WHERE  T1.LOCATION = T2.LOCATION
  AND  T1.STATUS   = T2.STATUS
  AND  T1.HOUR    <= T2.HOUR
GROUP BY T1.LOCATION, T1.STATUS, T1.HOUR 
    -- May need to add T2.LOCATION, T2.STATUS to GROUP BY???

Ever thought about a stored procedure? I think that would be the most readable solution.

Basic Idea:

  1. run a select statement that gives you the rown in the right order for one building
  2. iterate over the result line by line and write a new run -record every time the status changes and when reaching the end of the result set.

You need to test if it is also the fastest way. Depending on the number of records, this might not be an issue at all.





相关问题
Export tables from SQL Server to be imported to Oracle 10g

I m trying to export some tables from SQL Server 2005 and then create those tables and populate them in Oracle. I have about 10 tables, varying from 4 columns up to 25. I m not using any constraints/...

Connecting to Oracle 10g with ODBC from Excel VBA

The following code works. the connection opens fine but recordset.recordCount always returns -1 when there is data in the table. ANd If I try to call any methods/properties on recordset it crashes ...

How to make a one to one left outer join?

I was wondering, is there a way to make a kind of one to one left outer join: I need a join that matches say table A with table B, for each record on table A it must search for its pair on table B, ...

Insert if not exists Oracle

I need to be able to run an Oracle query which goes to insert a number of rows, but it also checks to see if a primary key exists and if it does, then it skips that insert. Something like: INSERT ALL ...

How can I store NULLs in NOT NULL field?

I just came across NULL values in NOT-NULL fields in our test database. How could they get there? I know that NOT-NULL constraints can be altered with NOVALIDATE clause, but that would change table s ...

Type reference scope

I m studying databases and am currently working on a object-relational DB project and I ve encountered a small problem with the number of possible constraints in an object table. I m using "Database ...

OracleParameter and DBNull.Value

we have a table in an Oracle Database which contains a column with the type Char(3 Byte). Now we use a parameterized sql to select some rows with a DBNull.Value and it doesn t work: OracleCommand ...

热门标签