4

The below is a table that is meant to show when a media will play. so basically it has a start time (starts), the length of the track (clip_length), and when it ends (ends = starts + clip_length), and finally the position of the track.

|starts              | ends                 |position   |file_id| clip_length
|2013-08-30 22:00:00 | 2013-08-30 22:03:08  |0          |16     |00:03:08.081768
|2013-08-30 22:03:08 | 2013-08-30 22:06:33  |1          |17     |00:03:25.436485
|2013-08-30 22:06:33 | 2013-08-30 22:09:07  |2          |7      |00:02:33.79968
|2013-08-30 22:09:07 | 2013-08-30 22:12:21  |3          |3      |00:03:14.020273
|2013-08-30 22:12:21 | 2013-08-30 22:15:31  |4          |8      |00:03:10.466689

what i want to do is to add a record, at say position =2 , shown bellow. I have been able to increment the positions, how ever the problem lies with the fact that the times are all messed up.

|starts              | ends                 |position   |file_id|clip_length
|2013-08-30 22:00:00 | 2013-08-30 22:03:08  |0          |16     |00:03:08.081768
|2013-08-30 22:03:08 | 2013-08-30 22:06:33  |1          |17     |00:03:25.436485
|2013-08-30 22:06:33 | 2013-08-30 22:09:07  |2          |7      |00:02:33.79968
|2013-08-30 22:06:33 | 2013-08-30 22:11:03  |3          |1      |00:04:30.006958
|2013-08-30 22:09:07 | 2013-08-30 22:12:21  |4          |3      |00:03:14.020273
|2013-08-30 22:12:21 | 2013-08-30 22:15:31  |5          |8      |00:03:10.466689

so it possible to use the first start time.. as point 00, and add clip_length to starts and save in ends, for the first one. then for the second one use the first ends value as the starts and do this recursively till the end (following the positions) .

thanks in advance..

4
  • Struggling to understand what you want. A running total of the intervals? Commented Sep 2, 2013 at 11:33
  • basically I want to add the 'clip_length" field to the 'start' field, and save it in the same row in the 'ends' field. then in the next row. use the previous time the song ended as the start time for the up comming song. Commented Sep 2, 2013 at 11:56
  • Side note: If these are local times and the app runs in a time zone that has DST, then you will have errors during the transitions. Commented Sep 2, 2013 at 17:28
  • well, the time shown here is at GMT time.. so the system as already converted it to GMT time... Commented Sep 4, 2013 at 9:34

3 Answers 3

4

SQL Fiddle

update clip c
set
    starts = s.starts,
    ends = s.ends
from (
    select
        starts,
        starts + clip_length as ends,
        file_id,
        position
    from (
        select
            '2013-08-30 22:00:00'::timestamp
            + sum(clip_length) over(order by position)
            - clip_length as starts,
            clip_length,
            file_id,
            position
        from clip
    ) s
) s
where c.file_id = s.file_id
Sign up to request clarification or add additional context in comments.

1 Comment

hey, i dont think i understand well. When i executed it, it changes all the values to sum(clip_length) for the 'ends' value and sum(clip_length) - last clip length as the 'starts' value. For all the rows its the same value
3

Your data model is pretty borked. You're storing at least two pieces of redundant information. You need starts and file_id and any one of clip_length, ends or position; they can each be calculated from each other.

Right now you are storing redundant data, which creates the problem you now have where the data is not internally consistent, it has conflicts within its self.

In this case it sounds like position is trusted and the others aren't. Here's what I'd do:

SELECT 
  (SELECT min(starts) FROM Sometable)
    + COALESCE(
       sum(clip_length) OVER all_rows_but_last,
       INTERVAL '0' second
    )
  AS starts,
  (SELECT min(starts) FROM Sometable)
    + COALESCE(
       sum(clip_length) OVER all_rows_but_last,
       INTERVAL '0' second
    ) + clip_length
  AS ends, 
  position, 
  clip_length
FROM Sometable
WINDOW all_rows_but_last AS (ORDER BY position ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING);

see: http://sqlfiddle.com/#!12/ca5fa/1

The principle here is to find the lowest start - the one known to be valid - and ignore the following ends and starts as redundant and useless. Instead, add the running total of the previous durations as the start, and the same running total plus the current interval as the end.

You'll notice that this isn't an update. That's because I think you should actually:

  • change position to a floating point value or large integer so you don't need to re-number when you insert an entry. If you put something between 1 and 2 give it position 0.5. Or start with 10000, 20000 etc so you can insert 15000.

  • Get rid of the ends column completely. calculate it from starts + clip_length

  • Move starts to a separate table where it's stored only once, for the series of segments. Calculate clip start times on the fly from the sum of the lengths of previous clips since the start.

  • Re-define the current table as a view over the two tables described above.

That's making a lot of guesses about your data and how you're using it, but you haven't said much about what you're doing.

2 Comments

:) , well sadly i don't not have control over the database(cannot remove columns). Not the design, no data dictionary . Its a third-party application. that I'm trying to develop an API for.
oh and the Field_id is not very relevant. its auto generated
1
with cte as (
    select
        '2013-08-30 22:00:00'::timestamp
         + sum(clip_length) over(order by position) as ends,
         file_id
    from clip
)
update clip as cl set
    starts = c.ends - cl.clip_length,
    ends = c.ends
from cte as c
where cl.file_id = c.file_id;

=>sql fiddle

Actually, you could live without start and end time in your table at all. You can remove this columns from your table and create function like this:

create or replace function clip_layout(_starts timestamp)
returns table(
    starts timestamp, ends timestamp,
    "position" int, file_id int,clip_length interval
)
as
$$
with cte as (
    select
        _starts + sum(clip_length) over(order by position) as ends,
        file_id, clip_length, position
    from clip
)
select
    ends - clip_length as starts,
    ends, position,
    file_id, clip_length
from cte
$$
language sql;

So you can see start/end times starting from anytime:

select * from clip_layout('2013-08-30 22:00:00'::timestamp)

=>sql fiddle demo

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.