Solved

Inner Join the same table to compare record against the previous record - again

Posted on 2004-09-02
8
528 Views
Last Modified: 2008-02-01
Well, I though I had this resolved in the following answer...

http://www.experts-exchange.com/Databases/Mysql/Q_21108778.html

But, when you do the Group By, it doesn't always work.  Continuing on the example info from before...

I am working with readings from a meter (like an electric meter), where the readings are on a totalizer, so to get the amount used between readings, you subtract the earlier reading from the later reading.

So, I am trying to do a sql statement that joins in the IMMEDIATE previous reading so you can subtract it.

The table (meter_data) with the essential fields and example data...

reading_id   read_date     read_time     reading
-----------    -----------      -----------      ---------
    1            2004-08-25    4:00:00         100
    2            2004-08-26    5:00:00         150
    3            2004-08-26    11:00:00       170
    4            2004-08-22    14:00:00        60

SELECT m1.read_date as end_date , m1.read_time as end_time , m2.read_date as start_date , m2.read_time as start_time , m1.reading - m2.reading as amount_used

FROM meter_data m1 inner join meter_data m2 on (m1.read_date > m2.read_date OR (m1.read_date = m2.read_date AND m1.read_time > m2.read_time))

GROUP BY  m2.read_date, m2.read_time

If you run this without the GROUP BY statement, you will get 6 records (3 previous records for reading_id of 3, 2 previous records for reading_id 2, 1 previous record for reading_id 1, and none for reading_id 4).

When you apply a GROUP BY, it doesn't have enough information to correctly group by, i.e. it doesn't know which of the 3 returned records for reading_id 3 is the right one (the immediate previously one).

I tried meddling around with timediff to find the smallest timediff, but kept going in circles.

I am trying to keep this in one sql statement, though if I have to, I will do two seperate SQL statments, ordered by read_date,read_time, and offset by 1 with a limit, and run through my math that way.

Any suggestions you have would be much appreciated.

Thanks.


0
Comment
Question by:keeper3
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 3
8 Comments
 
LVL 15

Expert Comment

by:JakobA
ID: 11969243
Your GROUP BY criteria are not consistent with the fields you ask for.

The fields (m1.read_date, m1.read_time) are likely to be different, eventhough (m2.read_date, m2.read_time) is the same, and when that happens those rows cannot be grouped.

So I would suggest another approach:

SELECT m1.read_date as end_date ,
            m1.read_time as end_time ,
            m2.read_date as start_date ,
            m2.read_time as start_time ,
            m1.reading - m2.reading as amount_used
FROM   meter_data m1 inner join meter_data m2
WHERE m1.reading_id = m2.reading_id -1              -- complex joiming conditions should be in the where clause

this will give you  each interval between readings. Note that the GROUP BY is unnessesarry (and this will run a LOT faster)

But it will not work if there are holes in the sequence of reading_id values, or if the -id ordering is not the same as the time ordering.  With an auto increment field and no DELETES that should be ok, but check it please.

regards JakobA
0
 

Author Comment

by:keeper3
ID: 11974245
Unfortunately, the reading_id values cannot be depended to be sequential because they are manually entered.  Someone might store up a couple readings on paper, and then enter them in an order that isn't necessarily chronological.

As for the GROUP BY not being consistent, agreed.  Each reading_id should end up with a single record, so you can group by the read_date/read_time, or the reading_id, but regardless of which, the statement is missing something to ensure correct grouping.
0
 
LVL 15

Expert Comment

by:JakobA
ID: 11976584
OK. there is another way to 'GROUP' them, but it may be quite slow, so test with a more than 4 entries table:

concatenate the 3 fields  read_date, read_time and reading from each row in each table. That gives us a compond field we can do a MAX on and then group with your original approach.

SELECT CONCAT( m1.read_date, m1.read_time, m1.reading) as m1conc,
            CONCAT( m2.read_date, m2.read_time, m2.reading) as m2conc,
FROM   meter_data m1 inner join meter_data m2
WHERE m1conc > m2conc
GROUP BY  m2conc

note that we cannot fetch the reading field as an independent value (it too differs within the groups), so you will have to extract it with programming from the resultset and then subtract to get the amount-used value.

regards JakobA

There is a possibility error here, I assume you are storing date and time in mysql's builtin DATE and TIME formats. If you are not the concatenation can go wrong as the time lose it initial zero with one digit hour values.

regards JakobA
0
Use Case: Protecting a Hybrid Cloud Infrastructure

Microsoft Azure is rapidly becoming the norm in dynamic IT environments. This document describes the challenges that organizations face when protecting data in a hybrid cloud IT environment and presents a use case to demonstrate how Acronis Backup protects all data.

 
LVL 15

Expert Comment

by:JakobA
ID: 11976627
Oops.  I forgot grouping the m1 values:

SELECT MAX( CONCAT( m1.read_date, m1.read_time, m1.reading) ) AS m1conc,
            CONCAT( m2.read_date, m2.read_time, m2.reading) AS m2conc,
FROM   meter_data m1 JOIN meter_data m2
GROUP BY  m2conc

0
 
LVL 15

Assisted Solution

by:JakobA
JakobA earned 250 total points
ID: 11976638
Darn. dumb again

SELECT MAX( CONCAT( m1.read_date, m1.read_time, m1.reading AS m1temp) ) AS m1conc,
            CONCAT( m2.read_date, m2.read_time, m2.reading) AS m2conc,
FROM   meter_data m1 JOIN meter_data m2
where   m1temp < m2conc
GROUP BY  m2conc
0
 

Author Comment

by:keeper3
ID: 11996238
Well, a couple things.  

1)  MySQL isn't liking, for me, putting in the alias m1temp inside a CONCAT
2)  Nor does it like having alias' in the WHERE clause.  Especially where the WHERE includes a MAX function, gives a invalid group function.

I have gotten a statement to work where I am working with a specific man_reading_id by doing the same type of join, but doing the following in my WHERE statement...

WHERE  m1.reading_id = 1 and m2.reading_id =  (select reading_id from meter_data m1 where (read_date = (select read_date from meter_data where reading_id = 1) and read_time < (select read_time from meter_data where reading_id = 1)) or (read_date < (select read_date from meter_data where reading_id = 1)) order by read_date desc,read_time desc limit 1)

This way I can be assured that my m2.reading_id is the immediate previous.  I don't see how to do this without specifying a specific reading_id.
0
 
LVL 2

Accepted Solution

by:
_kiew earned 250 total points
ID: 12030848
This should work:

SELECT m1.reading_id, m1.read_date, m1.read_time, MIN(m1.reading - m2.reading) AS amount_used
FROM meter_data m1, meter_data m2
ON (m1.read_date > m2.read_date OR (m1.read_date = m2.read_date AND m1.read_time > m2.read_time))
GROUP BY  m1.reading_id
0
 

Author Comment

by:keeper3
ID: 12404706
Well, I didn't end up getting it to work this way, but both of your answers did lead me in a different direction.

Thanks.
0

Featured Post

Simplifying Server Workload Migrations

This use case outlines the migration challenges that organizations face and how the Acronis AnyData Engine supports physical-to-physical (P2P), physical-to-virtual (P2V), virtual to physical (V2P), and cross-virtual (V2V) migration scenarios to address these challenges.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
paypal ipn to mysql 3 106
how to access a remote mysql database with xampp 3 73
Inserting data into database 10 52
MySQL to get Limited value from each category. 6 41
Load balancing is the method of dividing the total amount of work performed by one computer between two or more computers. Its aim is to get more work done in the same amount of time, ensuring that all the users get served faster.
Introduction This article is intended for those who are new to PHP error handling (https://www.experts-exchange.com/articles/11769/And-by-the-way-I-am-New-to-PHP.html).  It addresses one of the most common problems that plague beginning PHP develop…
Are you ready to implement Active Directory best practices without reading 300+ pages? You're in luck. In this webinar hosted by Skyport Systems, you gain insight into Microsoft's latest comprehensive guide, with tips on the best and easiest way…

739 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question