2011-09-09 18 views
3

我正在嘗試使用Oracle 11g(dev中爲11.1,生產中爲11.2)進行數值分析,特別是對有三列感興趣的表格(時間戳,設備ID和值)的線性插值。如何使用Oracle SQL執行線性插值?

值列保存來自設備的數據(ID爲deviceid),取自時間戳中給出的時間。例如,這是虛假的數據,但它給人的想法:

 time  | deviceid | value 
----------------|------------|----------- 
01:00:00.000 | 001  | 1.000 
01:00:01.000 | 001  | 1.030 
01:00:02.000 | 001  | 1.063 
01:00:00.050 | 002  | 553.10 
01:00:01.355 | 002  | 552.30 
01:00:02.155 | 002  | 552.43 

從設備001的時間戳不匹配裝置002的時間戳,但我需要從兩個設備001和002在一個有值行,有一個時間戳,匹配的設備001的時間戳是什麼我想落得這樣的:

 time  | device 001 | device 002 
----------------|--------------|------------ 
01:00:00.000 | 1.000  | null 
01:00:01.000 | 1.030  | 552.520 
01:00:02.000 | 1.063  | 552.405 

凡裝置002的值線性插值基礎上,值在收集裝置002設備001的每個時間戳任一側的兩個最接近的時間戳。 發生null是因爲我沒有兩次設備002在01:00:00.000任意一側出現篡改,我不想推斷該值。

從我的理解我可以使用percentile_cont來做到這一點,但我不明白我在網上看到的例子。例如,percentile_cont使用的百分位數是從哪裏來的?

在此先感謝您的幫助!

+0

感謝您的回答至今。我對Oracle分析函數非常陌生,我的數據並不像我給出的例子那樣整潔。例如,device1對device2上的每個值都有幾個值。對於一個設備,這些值的增量大約爲十分之一秒,而對於另一個設備則爲三分之一秒。編輯問題以提供更多詳細信息... – GLaDOS

回答

3

我不確定你如何使用PERCENTILE_CONT來做你想要的插值,但藉助不同的分析函數,你可以實現你想要的。

首先,我們將創建下列函數,它INTERVAL DAY TO SECOND值轉換成秒:

CREATE OR REPLACE FUNCTION intvl_to_seconds(
    p_interval INTERVAL DAY TO SECOND 
) RETURN NUMBER DETERMINISTIC 
AS 
BEGIN 
    RETURN EXTRACT(DAY FROM p_interval) * 24*60*60 
     + EXTRACT(HOUR FROM p_interval) * 60*60 
     + EXTRACT(MINUTE FROM p_interval) * 60 
     + EXTRACT(SECOND FROM p_interval); 
END; 
/

有了這個功能,我們可以使用查詢,如下列:

SELECT d1.time, 
     d1.value AS value1, 
     q2.prev_value + intvl_to_seconds(d1.time - q2.prev_time) * (q2.next_value - q2.prev_value)/intvl_to_seconds(q2.next_time - q2.prev_time) AS value2 
    FROM devices d1 
    LEFT OUTER JOIN (SELECT d2.time AS prev_time, 
          d2.value AS prev_value, 
          LEAD(d2.time, 1) OVER (ORDER BY d2.time) AS next_time, 
          LEAD(d2.value, 1) OVER (ORDER BY d2.time) AS next_value 
        FROM devices d2 
        WHERE d2.deviceid = 2) q2 
       ON d1.time BETWEEN q2.prev_time AND q2.next_time 
WHERE d1.deviceid = 1; 

我把你上面的數據,把時間戳的日期組件設置爲今天,當我運行上面的查詢時,我得到了以下結果:

 
TO_CHAR(D1.TIME)       VALUE1  VALUE2 
------------------------------------- ---------- ---------- 
09-SEP-11 01.00.00.000000      1 
09-SEP-11 01.00.01.000000     1.03 552.517625 
09-SEP-11 01.00.02.000000     1.063 552.404813 

(我加了一個TO_CHAR各地d1.time以減少在SQL * Plus過多空白。)

如果您使用DATE!而非TIMESTAMP S,你不需要的功能:你可以減日期。

+0

編寫函數的另一種方法是使用CAST(timestamp_column AS DATE)'。這可以避免大量的SQL到PL/SQL和後臺上下文切換。 –

+0

...不,我不能。如果我施放了日期,我會丟失小數秒數據。 – GLaDOS

+0

我一直在努力將時間戳轉換爲1970年1月1日以來的毫秒數;我實際上沒有權限在數據庫創建函數。 – GLaDOS

0

我使用@Luke伍德沃德的查詢的修改版本:

SELECT d1.time, 
    d1.value AS value1, 
    q2.prev_value + 
    (EXTRACT(SECOND FROM (d1.time - q2.prev_time)) + 
    EXTRACT(MINUTE FROM (d1.time - q2.prev_time)) * 60) 
    * (q2.next_value - q2.prev_value)/ 
     (EXTRACT (SECOND FROM (q2.next_time - q2.prev_time)) + 
     EXTRACT (MINUTE FROM (q2.next_time - q2.prev_time)) * 60) AS value2 
FROM devices d1 
LEFT OUTER JOIN (SELECT d2.time AS prev_time, 
         d2.value AS prev_value, 
         LEAD(d2.time, 1) OVER (ORDER BY d2.time) AS next_time, 
         LEAD(d2.value, 1) OVER (ORDER BY d2.time) AS next_value 
       FROM devices d2 
       WHERE d2.deviceid = 2 
         and time between '20100914 000000' and '20100915 000000' 
       ) q2 
      ON d1.time BETWEEN q2.prev_time AND q2.next_time 
WHERE d1.deviceid = 1; 

但插值總是上來爲空,即使是在日期範圍裝置2的數據。

請注意,我不得不在q2中添加查詢的日期範圍,這也許是爲什麼普通連接會丟失外部數據。

如果我使用正常連接,但是在使用正常連接時,我不會得到插值數據的空值,但是我丟失了設備1的端點之外的設備1的數據(q2中的插值設備) 。建議?

+0

請注意,請參閱上面的最終解決方案。再次感謝@Luke Woodward – GLaDOS

+0

下面的最終解決方案......訂單被切換了......可能再次切換......所以我使用的最終解決方案是「最終解決方案...」 – GLaDOS

0

的日期範圍的最終解決方案:

SELECT 
    d1.time, 
    d1.value AS value1, 
    q2.prev_value + 
    (EXTRACT(SECOND FROM (d1.time - q2.prev_time)) + 
    EXTRACT(MINUTE FROM (d1.time - q2.prev_time)) * 60) 
    * (q2.next_value - q2.prev_value)/ 
     (EXTRACT (SECOND FROM (q2.next_time - q2.prev_time)) + 
     EXTRACT (MINUTE FROM (q2.next_time - q2.prev_time)) * 60 
    ) AS value2 
FROM devices d1 
LEFT OUTER JOIN (
    SELECT d2.time AS prev_time, 
      d2.value AS prev_value, 
      LEAD(d2.time, 1) OVER (ORDER BY d2.time) AS next_time, 
      LEAD(d2.value, 1) OVER (ORDER BY d2.time) AS next_value 
    FROM devices d2 
    WHERE d2.deviceid = 2 
    AND time BETWEEN '20100914 000000' AND '20100915 000000' 
) q2 
ON d1.time BETWEEN q2.prev_time AND q2.next_time 
WHERE d1.deviceid = 1 
AND time BETWEEN '20100914 000000' AND '20100915 000000';