SQL Diff intervals

Diff intervals

-- Time series ranking

-- Set up table with random data
USE pubs
SET NOCOUNT ON
GO

IF NULLIF(OBJECT_ID('measurements'), 0) > 0
    DROP TABLE measurements
GO

CREATE TABLE measurements
(
when_taken     datetime      NOT NULL,
temperature    numeric(4,1)  -- (Fahrenheit)
)
CREATE CLUSTERED INDEX measurements_idx01
    ON measurements (when_taken)
GO

DECLARE @counter int, @whendate datetime, @val numeric(4, 1),
    @randdiff smallint, @randmins smallint
SELECT @counter=1, @whendate=GETDATE(), @val=50.0
/* Insert 20 rows of data.  Change constant if you want more.   */
WHILE (@counter <= 20)
    BEGIN
    INSERT measurements VALUES (@whendate, @val)
    --  Get a random number between -20 and 20 for change in
    -- temperature. This will be added to the previous value ,
    -- plus RAND() again to give a fractional component.
        SELECT 
        @randdiff=CASE
        WHEN CONVERT(int, RAND() * 100) % 2 = 1 THEN
            CONVERT(int, RAND() * 1000) % 21 * -1
        ELSE CONVERT(int, RAND() * 1000) % 21
        END,
    -- Get a random number between 0 and 10080 (the number of mins
    -- in a week). This will be added to the current GETDATE()
    -- value. Since GETDATE() returns a value to the millisecond
    -- it's very unlikely there will ever be a duplicate, though it
    -- is possible if the result of the additon and the current 
    -- GETDATE() value happen to collide with the addition in
    -- another row. (It is intentional that we are
    -- not assuming that dups are automatically prevented.)
        @randmins=CONVERT(int, RAND() * 100000) % 10080
SELECT @counter=@counter + 1,
@whendate=DATEADD(mi, @randmins, GETDATE()),
    @val=@val + @randdiff + RAND()
    END

SELECT * FROM measurements
GO

if nullif(object_id('rankdates'),0) > 0
	DROP VIEW rankdates
go

-- Approach 1: Standard SQL
CREATE VIEW rankdates (when_taken, temperature, daterank)
AS
SELECT when_taken, temperature,
    (SELECT COUNT(DISTINCT when_taken) FROM measurements AS T1
    WHERE T1.when_taken <= T0.when_taken) AS rank
FROM measurements AS T0                                                                    
GO
SELECT * FROM rankdates ORDER BY daterank
GO

-- Correlate each value with the one right before it
DECLARE @elapse datetime
SELECT @elapse=GETDATE()
SELECT 
P1_WHEN=V1.when_taken, P2_WHEN=V2.when_taken, 
P1=V1.temperature, P2=V2.temperature,
DIFF=(V2.temperature - V1.temperature)
FROM rankdates AS V1 LEFT OUTER JOIN rankdates AS V2
ON (V2.daterank=V1.daterank + 1)
SELECT 'Elapsed Time for view'=DATEDIFF(ms, @elapse, GETDATE())
GO

-- Approach 2: Materialize rankings in temp table and then self-join
CREATE TABLE #rankdates (
when_taken datetime,
temperature numeric(4, 1),
daterank int IDENTITY PRIMARY KEY)
GO

INSERT #rankdates (when_taken, temperature)
    SELECT when_taken, temperature 
    FROM measurements
    ORDER BY when_taken ASC
GO

SELECT 
P1_WHEN=V1.when_taken, P2_WHEN=V2.when_taken, 
P1=V1.temperature, P2=V2.temperature,
DIFF=(V2.temperature - V1.temperature)
FROM #rankdates AS V1 LEFT OUTER JOIN #rankdates AS V2
ON (V2.daterank=V1.daterank + 1)
GO