lead() 和 lag() 函數
Oracle的兩個與偏移量相關的分析函數。
可以獲取到向前或向後偏移指定行的某一列元素。
效果類似於自連接,但使用更加方便,效率更高。
-
lead(field, number, default) over(partition by id order by name)
當前記錄後 number 條記錄的 field 字段的值,如果沒有默認爲 default -
lag(field, number, default) over(partition by id order by name)
當前記錄前 number 條記錄的 field 字段的值,如果沒有默認爲 default
示例一
有 n 條生產線生產 2 中產品(檯燈、手機),每條生產線上有 1 個計數器,每生產一個產品,計數器就會 + 1(計數器最大值爲 999, 即每到 1000 就會清零重新開始)。☆ 假設獲取計數器值的頻率很高,排除瞬間增長 >1000個產品的情況。
-
構造原始數據
-
統計每次獲取計數器時數字的增量。
使用當前記錄的 COUNT 值 減去前一條記錄 的 COUNT 值,如果沒有,默認等於當前記錄,如當前記錄的 COUNT 大於 前一條記錄的 COUNT,則意味着被置零,需要加上 1000/*此段代碼爲測試數據構造 start*/ WITH temp AS ( SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 500 COUNT FROM dual UNION ALL SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 750 COUNT FROM dual UNION ALL SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:20:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 945 COUNT FROM dual UNION ALL SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 130 COUNT FROM dual UNION ALL SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 425 COUNT FROM dual UNION ALL SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:50:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 654 COUNT FROM dual UNION ALL SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 22:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 32 COUNT FROM dual UNION ALL SELECT '計數器-02' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 412 COUNT FROM dual UNION ALL SELECT '計數器-02' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 521 COUNT FROM dual UNION ALL SELECT '計數器-02' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:50:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 687 COUNT FROM dual UNION ALL SELECT '計數器-02' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 22:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 873 COUNT FROM dual UNION ALL SELECT '計數器-02' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 22:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 952 COUNT FROM dual UNION ALL SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 43 COUNT FROM dual UNION ALL SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 241 COUNT FROM dual UNION ALL SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:20:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 434 COUNT FROM dual UNION ALL SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 644 COUNT FROM dual UNION ALL SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 832 COUNT FROM dual UNION ALL SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:50:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 5 COUNT FROM dual UNION ALL SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 23:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 231 COUNT FROM dual UNION ALL SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 21:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 496 COUNT FROM dual UNION ALL SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 21:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 634 COUNT FROM dual UNION ALL SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 21:50:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 874 COUNT FROM dual UNION ALL SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 969 COUNT FROM dual UNION ALL SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 201 COUNT FROM dual UNION ALL SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:20:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 423 COUNT FROM dual UNION ALL SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 654 COUNT FROM dual UNION ALL SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 888 COUNT FROM dual ) /*此段代碼爲數據構造 end*/ SELECT R.CNAME, R.ENAME, R.CDATE, R.CURRENT_COUNT, R.LAST_COUNT, DECODE(SIGN(R.CURRENT_COUNT - R.LAST_COUNT), -1, R.CURRENT_COUNT - R.LAST_COUNT + 1000, R.CURRENT_COUNT - R.LAST_COUNT) 增量 FROM (SELECT T.CNAME, T.ENAME, T.CDATE, T.COUNT CURRENT_COUNT, LAG(T.COUNT, 1, T.COUNT) OVER(PARTITION BY T.CNAME, T.ENAME ORDER BY T.CDATE) LAST_COUNT FROM TEMP T) R;
示例二
有 n 臺檢測器,隔一段時間抓取一次狀態信息,狀態有兩種,正常(normal)和異常(error)。獲取異常時間段。
-
構造原始數據
-
統計當前記錄的上一個狀態和下一個狀態。
假設統計頻率足夠高,忽略狀態轉換之間的之間。
那麼,要獲取異常區間。
自身狀態爲 error 並且上一個狀態爲 normal 的時間,爲異常開始時間
自身狀態爲 error 並且下一個狀態爲 normal 的時間,爲異常結束時間/*此段代碼爲測試數據構造 start*/ WITH temp AS( SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 21:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'normal' STATUS FROM dual UNION ALL SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 21:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'normal' STATUS FROM dual UNION ALL SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 21:50:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'error' STATUS FROM dual UNION ALL SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 22:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'error' STATUS FROM dual UNION ALL SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 22:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'error' STATUS FROM dual UNION ALL SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 22:20:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'normal' STATUS FROM dual UNION ALL SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 22:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'normal' STATUS FROM dual UNION ALL SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 22:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'error' STATUS FROM dual UNION ALL SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 22:50:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'error' STATUS FROM dual UNION ALL SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 23:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'normal' STATUS FROM dual UNION ALL SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 23:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'error' STATUS FROM dual UNION ALL SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 23:20:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'normal' STATUS FROM dual ) /*此段代碼爲測試數據構造 end*/ SELECT R.CNAME, R.CDATE, R.STATUS, /* 要獲取異常區間 */ /*自身狀態爲 error 並且上一個狀態爲 normal 的時間 爲異常開始時間 */ /*自身狀態爲 error 並且下一個狀態爲 normal 的時間 爲異常結束時間 */ DECODE(R.STATUS, 'error',DECODE(R.LAST_STATUS, 'normal', R.CDATE, NULL) , NULL ) ERR_START_DATE, DECODE(R.STATUS, 'error', DECODE(R.NEXT_STATUS, 'normal', R.CDATE, NULL ), NULL) ERR_END_DATE FROM ( SELECT T.CNAME, T.CDATE, LAG(T.STATUS, 1, T.STATUS) OVER(PARTITION BY T.CNAME ORDER BY T.CDATE) LAST_STATUS, T.STATUS, LEAD(T.STATUS, 1, T.STATUS) OVER(PARTITION BY T.CNAME ORDER BY T.CDATE) NEXT_STATUS FROM TEMP T ) R;