On Sun, Oct 20, 2019 at 8:23 PM Petr Jakeš <petr.jakes....@gmail.com> wrote:

>
>
> On Sun, Oct 20, 2019 at 4:36 PM Keith Medcalf <kmedc...@dessus.com> wrote:
>
>>
>> On Sunday, 20 October, 2019 06:58, Petr Jakeš <petr.jakes....@gmail.com>
>> wrote:
>>
>> >On Sun, Oct 20, 2019 at 2:53 AM Keith Medcalf <kmedc...@dessus.com>
>> wrote:
>>
>> >> On Saturday, 19 October, 2019 18:26, Petr Jakeš <
>> petr.jakes....@gmail.com> wrote:
>>
>> >>> After long time I have set up development environment properly and I
>> >>> am able to start to study your queries.
>>
>> >>> I am lost. I don't either understand the first bunch of subqueries...
>> >>> (What is returned in the "ratetoprior"? I have been pulling my hair
>> >>> over 3 hours trying to figure it out ... no clue what it means.
>>
>> >> RateToPrior is the linear slope of the line which joins the current
>> >> value to the prior value.  Once you know the slope of that line, then
>> for any
>> >> point in time you merely need to find the next reading after that point
>> >> in time and use the slope to calculate/interpolate what the value would
>> >> have been at the time you are interested in (assuming that the value
>> change
>> >> is linear).
>>
>> >> Does this help?
>>
>> > If I understand it properly:
>>
>> >   - timestamps are considered as x values, readings as y values
>> >   - *pwr* statement calculates slope of the line
>> >   <https://en.wikipedia.org/wiki/Slope> (delta y / delta x) between all
>> >   adjacent readings
>> >   - *periods* statement calculates (fills in) the dates according to the
>> >   requested granity
>> >   - *readings* statement calculates readings from the next reading (next
>> >   higher timestamp to the periods datestamp)
>> >   - *used* statement calculates consumption (lag between two adjacent
>> >   rows)
>>
>> > My confusion was I didn't expect all values are calculated, not just
>> > returned directly from the database as I was almost doing.
>> > Your approach is GENIAL! Thank you.
>>
>> You got it exactly.  It really does not matter if one of your requested
>> points (x) happens to match an actual sample, as the calculated result from
>> the next sample (y) should match that value because it is interpolating
>> backwards -- it is also easier to always calculate the appropriate y for
>> the given x, than it is to figure out where the x samples are and fill in
>> the blanks.
>>
>> > Do you mind if I will mention you/your solution on my blog (in the time
>> > when I write post about power/water metering)?
>>
>> Not at all.
>>
>> > What about if I want 1 hour granity? (to plot a graph of daily
>> > consumption for example)
>>
>> This should work exactly the same way.  You just need to generate the
>> *periods* so that they match the x values for which you want to calculate
>> readings.  Since the *used* table reflects the lag between two adjacent
>> rows, you need your *periods* to include the last hour of the previous day
>> and perhaps the first hour of the next day (so 26 points, as it were), and
>> then filter it afterwards when you plot your graph.  Note that this will be
>> a "snapshot" based graph, not an "average" based graph.
>>
>> This scheme is generally how Process Historians work for continuous
>> control.  Once each minute (the exact offset into the minute does not
>> really matter nor does it need to be the same, it is just frequent
>> readings) the instantaneous value is collected and stored together with the
>> computed slope to the previous value.  From this you can interpolate what
>> the instantaneous value was at exactly each minute boundary.  This gives
>> you a series of polygons which are basically estimates of the shape of the
>> curve.  The historian will generate the one minute "snapshot" values and
>> average those to get each hourly average.  It can also compute the
>> "confidence" in the accuracy of that value based on the number of actual
>> samples in the interval (vs the number there ought to be).  Industry
>> standard values are called "raw" which is the actual recorded data with no
>> interpolation, "snapshot" (point in time interpolated values), one minute
>> average (which is different from the snapshot since the value usually
>> changed sometime during the minute and the average is computed assuming
>> that the prior value was in effect up until the change, and the new value
>> afterwards, ie, that the readings are discrete -- or it may average the
>> start of minute and the end of minute snapshots depending on
>> configuration), six minute average on every 6 minute boundary (the average
>> of the 6 one minute snapshot values (at minute offset 0, 6, 12 ... in the
>> hour), and hourly average on every hour (the average of the 60 one minute
>> snapshot values).  Intervals less than a day the y (timestamp) is the at
>> the end of the interval, and for intervals greater than a day the y
>> (timestamp) is the beginning of the interval.  Sometimes the sample
>> frequency is increased beyond 1 minute, in which case the calculations are
>> all based on that sample frequency giving you a better approximation of the
>> curve.  (It is all really nothing more than just using Eulers method to
>> collect samples which will allow an integral to be approximated).
>>
>
> Thank you for nice lesson. I was briefly googling about Process
> Historians. Thanks to your help I know, SQLite is powerful enough for my
> purposes (to store 9 water meters  and 9 power meters readings). All
> depends, of course, about the time granularity of stored readings. In my
> case, one reading a day (water) and 144 readings a day (energy) per spot
> will be enough.
>

1 hour granularity works as expected...

         select julianday(strftime('%Y-%m-%d %H',(min(timestamp)), '-1
hour') || ':00:00.000')
           from pwr
      union all
         select julianday(datetime(timestamp, '+1 hour'))
           from periods
          where timestamp < (select max(timestamp) from pwr)


>
>
>
>> >
>> >
>> >> >with pwr (timestamp, reading, ratetoprior) as
>> >> >     (
>> >> >        select julianday(timestamp),
>> >> >               total_kwh,
>> >> >               (
>> >> >  select  (c.total_kwh - p.total_kwh) / (julianday(c.timestamp)-
>> >> >julianday(p.timestamp))
>> >> >                  from power as p
>> >> >                 where julianday(p.timestamp) < julianday(c.timestamp)
>> >> >              order by julianday(p.timestamp) desc
>> >> >                 limit 1
>> >> >)
>> >> >          from power as c
>> >> >      order by julianday(timestamp)
>> >> >     )
>> >> >select * from pwr
>> >> >
>> >> >On Tue, Sep 3, 2019 at 1:11 AM Keith Medcalf <kmedc...@dessus.com>
>> >wrote:
>> >> >
>> >> >>
>> >> >> On Monday, 2 September, 2019 12:26, Petr Jakeš
>> >> ><petr.jakes....@gmail.com>
>> >> >> wrote:
>> >> >>
>> >> >> >Yes, you are right. The error is connected with the version  of
>> >> >> >SQLite. Now I am trying to build DB Browser using SQLite version
>> >> >3.29.0.
>> >> >> >Than I have to study your code. Your knowledge and SQL Windows
>> >> >> >functions are over my scope. Thank for the study material for next
>> >> >> weekend :D
>> >> >>
>> >> >> Here is another example for you to ponder that uses your original
>> >table
>> >> >> and requires one index.  It builds the "pwr" view dynamically but
>> >uses
>> >> >the
>> >> >> ratetoprior to compute the instant readings and only needs one
>> >window
>> >> >query
>> >> >> to compute the usage from two readings.  Performance is half way
>> >> >between
>> >> >> the other two examples:
>> >> >>
>> >> >> create index if not exists power_jd on power (julianday(timestamp),
>> >> >> total_kwh);
>> >> >>
>> >> >> with pwr (timestamp, reading, ratetoprior) as
>> >> >>      (
>> >> >>         select julianday(timestamp),
>> >> >>                total_kwh,
>> >> >>                (select (c.total_kwh - p.total_kwh) /
>> >> >> (julianday(c.timestamp) - julianday(p.timestamp))
>> >> >>                   from power as p
>> >> >>                  where julianday(p.timestamp) <
>> >julianday(c.timestamp)
>> >> >>               order by julianday(p.timestamp) desc
>> >> >>                  limit 1)
>> >> >>           from power as c
>> >> >>       order by julianday(timestamp)
>> >> >>      ),
>> >> >>      periods (timestamp) as
>> >> >>      (
>> >> >>          select julianday(date(min(timestamp), '-1 day') || '
>> >> >> 23:59:59.999')
>> >> >>            from pwr
>> >> >>       union all
>> >> >>          select julianday(datetime(timestamp, '+1 day'))
>> >> >>            from periods
>> >> >>           where timestamp < (select max(timestamp) from pwr)
>> >> >>      ),
>> >> >>      readings (timestamp, reading) as
>> >> >>      (
>> >> >>       select timestamp,
>> >> >>              (select reading - (b.timestamp - p.timestamp) *
>> >> >ratetoprior
>> >> >>                from pwr as b
>> >> >>               where b.timestamp >= p.timestamp
>> >> >>               limit 1) as reading
>> >> >>         from periods as p
>> >> >>        where timestamp between (select min(timestamp) from pwr)
>> >> >>                            and (select max(timestamp) from pwr)
>> >> >>     ),
>> >> >>     used (timestamp, kwh) as
>> >> >>     (
>> >> >>       select timestamp,
>> >> >>              reading - lag(reading) over ()
>> >> >>         from readings
>> >> >>     )
>> >> >>   select datetime(timestamp),
>> >> >>          kwh
>> >> >>     from used
>> >> >>    where kwh is not null;
>> >> >>
>>
>> --
>> The fact that there's a Highway to Hell but only a Stairway to Heaven
>> says a lot about anticipated traffic volume.
>>
>>
>>
>> _______________________________________________
>> sqlite-users mailing list
>> sqlite-users@mailinglists.sqlite.org
>> http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
>>
>
_______________________________________________
sqlite-users mailing list
sqlite-users@mailinglists.sqlite.org
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to